File indexing completed on 2024-05-05 05:57:01

0001 /*
0002   SPDX-FileCopyrightText: 2008-2014 Eike Hein <hein@kde.org>
0003   SPDX-FileCopyrightText: 2009 Juan Carlos Torres <carlosdgtorres@gmail.com>
0004 
0005   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #include "tabbar.h"
0009 #include "mainwindow.h"
0010 #include "session.h"
0011 #include "sessionstack.h"
0012 #include "settings.h"
0013 #include "skin.h"
0014 
0015 #include <KActionCollection>
0016 #include <KLocalizedString>
0017 
0018 #include <QApplication>
0019 #include <QDBusConnection>
0020 #include <QFontDatabase>
0021 #include <QLineEdit>
0022 #include <QMenu>
0023 #include <QPainter>
0024 #include <QPushButton>
0025 #include <QToolButton>
0026 #include <QWhatsThis>
0027 #include <QWheelEvent>
0028 
0029 #include <QDrag>
0030 #include <QLabel>
0031 #include <QMimeData>
0032 
0033 TabBar::TabBar(MainWindow *mainWindow)
0034     : QWidget(mainWindow)
0035 {
0036     QDBusConnection::sessionBus().registerObject(QStringLiteral("/yakuake/tabs"), this, QDBusConnection::ExportScriptableSlots);
0037 
0038     setWhatsThis(xi18nc("@info:whatsthis",
0039                         "<title>Tab Bar</title>"
0040                         "<para>The tab bar allows you to switch between sessions. You can double-click a tab to edit its label.</para>"));
0041 
0042     m_selectedSessionId = -1;
0043     m_renamingSessionId = -1;
0044 
0045     m_mousePressed = false;
0046     m_mousePressedIndex = -1;
0047 
0048     m_dropIndicator = nullptr;
0049 
0050     m_mainWindow = mainWindow;
0051 
0052     m_skin = mainWindow->skin();
0053     connect(m_skin, SIGNAL(iconChanged()), this, SLOT(repaint()));
0054 
0055     m_tabContextMenu = new QMenu(this);
0056     connect(m_tabContextMenu, SIGNAL(hovered(QAction *)), this, SLOT(contextMenuActionHovered(QAction *)));
0057 
0058     m_toggleKeyboardInputMenu = new QMenu(xi18nc("@title:menu", "Disable Keyboard Input"), this);
0059     m_toggleMonitorActivityMenu = new QMenu(xi18nc("@title:menu", "Monitor for Activity"), this);
0060     m_toggleMonitorSilenceMenu = new QMenu(xi18nc("@title:menu", "Monitor for Silence"), this);
0061 
0062     m_sessionMenu = new QMenu(this);
0063     connect(m_sessionMenu, SIGNAL(aboutToShow()), this, SLOT(readySessionMenu()));
0064 
0065     m_newTabButton = new QToolButton(this);
0066     m_newTabButton->setFocusPolicy(Qt::NoFocus);
0067     m_newTabButton->setMenu(m_sessionMenu);
0068     m_newTabButton->setPopupMode(QToolButton::DelayedPopup);
0069     m_newTabButton->setToolTip(xi18nc("@info:tooltip", "New Session"));
0070     m_newTabButton->setWhatsThis(xi18nc("@info:whatsthis", "Adds a new session. Press and hold to select session type from menu."));
0071     connect(m_newTabButton, SIGNAL(clicked()), this, SIGNAL(newTabRequested()));
0072 
0073     m_closeTabButton = new QPushButton(this);
0074     m_closeTabButton->setFocusPolicy(Qt::NoFocus);
0075     m_closeTabButton->setToolTip(xi18nc("@info:tooltip", "Close Session"));
0076     m_closeTabButton->setWhatsThis(xi18nc("@info:whatsthis", "Closes the active session."));
0077     connect(m_closeTabButton, SIGNAL(clicked()), this, SLOT(closeTabButtonClicked()));
0078 
0079     m_lineEdit = new QLineEdit(this);
0080     m_lineEdit->setFrame(false);
0081     m_lineEdit->setClearButtonEnabled(false);
0082     m_lineEdit->setAlignment(Qt::AlignHCenter);
0083     m_lineEdit->hide();
0084 
0085     connect(m_lineEdit, SIGNAL(editingFinished()), m_lineEdit, SLOT(hide()));
0086     connect(m_lineEdit, SIGNAL(returnPressed()), this, SLOT(interactiveRenameDone()));
0087 
0088     setAcceptDrops(true);
0089 }
0090 
0091 TabBar::~TabBar()
0092 {
0093 }
0094 
0095 void TabBar::applySkin()
0096 {
0097     resize(width(), m_skin->tabBarBackgroundImage().height());
0098 
0099     m_newTabButton->setStyleSheet(m_skin->tabBarNewTabButtonStyleSheet());
0100     m_closeTabButton->setStyleSheet(m_skin->tabBarCloseTabButtonStyleSheet());
0101 
0102     moveNewTabButton();
0103     m_closeTabButton->move(width() - m_skin->tabBarCloseTabButtonPosition().x(), m_skin->tabBarCloseTabButtonPosition().y());
0104     repaint();
0105 }
0106 
0107 void TabBar::readyTabContextMenu()
0108 {
0109     if (m_tabContextMenu->isEmpty()) {
0110         m_tabContextMenu->addAction(m_mainWindow->actionCollection()->action(QStringLiteral("split-left-right")));
0111         m_tabContextMenu->addAction(m_mainWindow->actionCollection()->action(QStringLiteral("split-top-bottom")));
0112         m_tabContextMenu->addSeparator();
0113         m_tabContextMenu->addAction(m_mainWindow->actionCollection()->action(QStringLiteral("edit-profile")));
0114         m_tabContextMenu->addAction(m_mainWindow->actionCollection()->action(QStringLiteral("rename-session")));
0115         m_tabContextMenu->addAction(m_mainWindow->actionCollection()->action(QStringLiteral("toggle-session-prevent-closing")));
0116         m_tabContextMenu->addMenu(m_toggleKeyboardInputMenu);
0117         m_tabContextMenu->addMenu(m_toggleMonitorActivityMenu);
0118         m_tabContextMenu->addMenu(m_toggleMonitorSilenceMenu);
0119         m_tabContextMenu->addSeparator();
0120         m_tabContextMenu->addAction(m_mainWindow->actionCollection()->action(QStringLiteral("move-session-left")));
0121         m_tabContextMenu->addAction(m_mainWindow->actionCollection()->action(QStringLiteral("move-session-right")));
0122         m_tabContextMenu->addSeparator();
0123         m_tabContextMenu->addAction(m_mainWindow->actionCollection()->action(QStringLiteral("close-active-terminal")));
0124         m_tabContextMenu->addAction(m_mainWindow->actionCollection()->action(QStringLiteral("close-session")));
0125     }
0126 }
0127 
0128 void TabBar::readySessionMenu()
0129 {
0130     if (m_sessionMenu->isEmpty()) {
0131         m_sessionMenu->addAction(m_mainWindow->actionCollection()->action(QStringLiteral("new-session")));
0132         m_sessionMenu->addSeparator();
0133         m_sessionMenu->addAction(m_mainWindow->actionCollection()->action(QStringLiteral("new-session-two-horizontal")));
0134         m_sessionMenu->addAction(m_mainWindow->actionCollection()->action(QStringLiteral("new-session-two-vertical")));
0135         m_sessionMenu->addAction(m_mainWindow->actionCollection()->action(QStringLiteral("new-session-quad")));
0136     }
0137 }
0138 
0139 void TabBar::updateMoveActions(int index)
0140 {
0141     if (index == -1)
0142         return;
0143 
0144     m_mainWindow->actionCollection()->action(QStringLiteral("move-session-left"))->setEnabled(false);
0145     m_mainWindow->actionCollection()->action(QStringLiteral("move-session-right"))->setEnabled(false);
0146 
0147     if (index != m_tabs.indexOf(m_tabs.first()))
0148         m_mainWindow->actionCollection()->action(QStringLiteral("move-session-left"))->setEnabled(true);
0149 
0150     if (index != m_tabs.indexOf(m_tabs.last()))
0151         m_mainWindow->actionCollection()->action(QStringLiteral("move-session-right"))->setEnabled(true);
0152 }
0153 
0154 void TabBar::updateToggleActions(int sessionId)
0155 {
0156     if (sessionId == -1)
0157         return;
0158 
0159     KActionCollection *actionCollection = m_mainWindow->actionCollection();
0160     SessionStack *sessionStack = m_mainWindow->sessionStack();
0161 
0162     QAction *toggleAction = actionCollection->action(QStringLiteral("toggle-session-prevent-closing"));
0163     toggleAction->setChecked(!sessionStack->isSessionClosable(sessionId));
0164 
0165     toggleAction = actionCollection->action(QStringLiteral("toggle-session-keyboard-input"));
0166     toggleAction->setChecked(!sessionStack->hasTerminalsWithKeyboardInputEnabled(sessionId));
0167 
0168     toggleAction = actionCollection->action(QStringLiteral("toggle-session-monitor-activity"));
0169     toggleAction->setChecked(!sessionStack->hasTerminalsWithMonitorActivityDisabled(sessionId));
0170 
0171     toggleAction = actionCollection->action(QStringLiteral("toggle-session-monitor-silence"));
0172     toggleAction->setChecked(!sessionStack->hasTerminalsWithMonitorSilenceDisabled(sessionId));
0173 }
0174 
0175 void TabBar::updateToggleKeyboardInputMenu(int sessionId)
0176 {
0177     if (!m_tabs.contains(sessionId))
0178         return;
0179 
0180     QAction *toggleKeyboardInputAction = m_mainWindow->actionCollection()->action(QStringLiteral("toggle-session-keyboard-input"));
0181     QAction *anchor = m_toggleKeyboardInputMenu->menuAction();
0182 
0183     SessionStack *sessionStack = m_mainWindow->sessionStack();
0184 
0185     QStringList terminalIds = sessionStack->terminalIdsForSessionId(sessionId).split(QLatin1Char(','), Qt::SkipEmptyParts);
0186 
0187     m_toggleKeyboardInputMenu->clear();
0188 
0189     if (terminalIds.count() <= 1) {
0190         toggleKeyboardInputAction->setText(xi18nc("@action", "Disable Keyboard Input"));
0191         m_tabContextMenu->insertAction(anchor, toggleKeyboardInputAction);
0192         m_toggleKeyboardInputMenu->menuAction()->setVisible(false);
0193     } else {
0194         toggleKeyboardInputAction->setText(xi18nc("@action", "For This Session"));
0195         m_toggleKeyboardInputMenu->menuAction()->setVisible(true);
0196 
0197         m_tabContextMenu->removeAction(toggleKeyboardInputAction);
0198         m_toggleKeyboardInputMenu->addAction(toggleKeyboardInputAction);
0199 
0200         m_toggleKeyboardInputMenu->addSeparator();
0201 
0202         int count = 0;
0203 
0204         QStringListIterator i(terminalIds);
0205 
0206         while (i.hasNext()) {
0207             int terminalId = i.next().toInt();
0208 
0209             ++count;
0210 
0211             QAction *action = m_toggleKeyboardInputMenu->addAction(xi18nc("@action", "For Terminal %1", count));
0212             action->setCheckable(true);
0213             action->setChecked(!sessionStack->isTerminalKeyboardInputEnabled(terminalId));
0214             action->setData(terminalId);
0215             connect(action, SIGNAL(triggered(bool)), m_mainWindow, SLOT(handleToggleTerminalKeyboardInput(bool)));
0216         }
0217     }
0218 }
0219 
0220 void TabBar::updateToggleMonitorActivityMenu(int sessionId)
0221 {
0222     if (!m_tabs.contains(sessionId))
0223         return;
0224 
0225     QAction *toggleMonitorActivityAction = m_mainWindow->actionCollection()->action(QStringLiteral("toggle-session-monitor-activity"));
0226     QAction *anchor = m_toggleMonitorActivityMenu->menuAction();
0227 
0228     SessionStack *sessionStack = m_mainWindow->sessionStack();
0229 
0230     QStringList terminalIds = sessionStack->terminalIdsForSessionId(sessionId).split(QLatin1Char(','), Qt::SkipEmptyParts);
0231 
0232     m_toggleMonitorActivityMenu->clear();
0233 
0234     if (terminalIds.count() <= 1) {
0235         toggleMonitorActivityAction->setText(xi18nc("@action", "Monitor for Activity"));
0236         m_tabContextMenu->insertAction(anchor, toggleMonitorActivityAction);
0237         m_toggleMonitorActivityMenu->menuAction()->setVisible(false);
0238     } else {
0239         toggleMonitorActivityAction->setText(xi18nc("@action", "In This Session"));
0240         m_toggleMonitorActivityMenu->menuAction()->setVisible(true);
0241 
0242         m_tabContextMenu->removeAction(toggleMonitorActivityAction);
0243         m_toggleMonitorActivityMenu->addAction(toggleMonitorActivityAction);
0244 
0245         m_toggleMonitorActivityMenu->addSeparator();
0246 
0247         int count = 0;
0248 
0249         QStringListIterator i(terminalIds);
0250 
0251         while (i.hasNext()) {
0252             int terminalId = i.next().toInt();
0253 
0254             ++count;
0255 
0256             QAction *action = m_toggleMonitorActivityMenu->addAction(xi18nc("@action", "In Terminal %1", count));
0257             action->setCheckable(true);
0258             action->setChecked(sessionStack->isTerminalMonitorActivityEnabled(terminalId));
0259             action->setData(terminalId);
0260             connect(action, SIGNAL(triggered(bool)), m_mainWindow, SLOT(handleToggleTerminalMonitorActivity(bool)));
0261         }
0262     }
0263 }
0264 
0265 void TabBar::updateToggleMonitorSilenceMenu(int sessionId)
0266 {
0267     if (!m_tabs.contains(sessionId))
0268         return;
0269 
0270     QAction *toggleMonitorSilenceAction = m_mainWindow->actionCollection()->action(QStringLiteral("toggle-session-monitor-silence"));
0271     QAction *anchor = m_toggleMonitorSilenceMenu->menuAction();
0272 
0273     SessionStack *sessionStack = m_mainWindow->sessionStack();
0274 
0275     QStringList terminalIds = sessionStack->terminalIdsForSessionId(sessionId).split(QLatin1Char(','), Qt::SkipEmptyParts);
0276 
0277     m_toggleMonitorSilenceMenu->clear();
0278 
0279     if (terminalIds.count() <= 1) {
0280         toggleMonitorSilenceAction->setText(xi18nc("@action", "Monitor for Silence"));
0281         m_tabContextMenu->insertAction(anchor, toggleMonitorSilenceAction);
0282         m_toggleMonitorSilenceMenu->menuAction()->setVisible(false);
0283     } else {
0284         toggleMonitorSilenceAction->setText(xi18nc("@action", "In This Session"));
0285         m_toggleMonitorSilenceMenu->menuAction()->setVisible(true);
0286 
0287         m_tabContextMenu->removeAction(toggleMonitorSilenceAction);
0288         m_toggleMonitorSilenceMenu->addAction(toggleMonitorSilenceAction);
0289 
0290         m_toggleMonitorSilenceMenu->addSeparator();
0291 
0292         int count = 0;
0293 
0294         QStringListIterator i(terminalIds);
0295 
0296         while (i.hasNext()) {
0297             int terminalId = i.next().toInt();
0298 
0299             ++count;
0300 
0301             QAction *action = m_toggleMonitorSilenceMenu->addAction(xi18nc("@action", "In Terminal %1", count));
0302             action->setCheckable(true);
0303             action->setChecked(sessionStack->isTerminalMonitorSilenceEnabled(terminalId));
0304             action->setData(terminalId);
0305             connect(action, SIGNAL(triggered(bool)), m_mainWindow, SLOT(handleToggleTerminalMonitorSilence(bool)));
0306         }
0307     }
0308 }
0309 
0310 void TabBar::contextMenuActionHovered(QAction *action)
0311 {
0312     bool ok = false;
0313 
0314     if (!action->data().isNull()) {
0315         int terminalId = action->data().toInt(&ok);
0316 
0317         if (ok)
0318             Q_EMIT requestTerminalHighlight(terminalId);
0319     } else if (!ok)
0320         Q_EMIT requestRemoveTerminalHighlight();
0321 }
0322 
0323 void TabBar::contextMenuEvent(QContextMenuEvent *event)
0324 {
0325     if (event->x() < 0)
0326         return;
0327 
0328     int index = tabAt(event->x());
0329 
0330     if (index == -1)
0331         m_sessionMenu->exec(QCursor::pos());
0332     else {
0333         readyTabContextMenu();
0334 
0335         updateMoveActions(index);
0336 
0337         int sessionId = sessionAtTab(index);
0338         updateToggleActions(sessionId);
0339         updateToggleKeyboardInputMenu(sessionId);
0340         updateToggleMonitorActivityMenu(sessionId);
0341         updateToggleMonitorSilenceMenu(sessionId);
0342 
0343         m_mainWindow->setContextDependentActionsQuiet(true);
0344 
0345         QAction *action = m_tabContextMenu->exec(QCursor::pos());
0346 
0347         Q_EMIT tabContextMenuClosed();
0348 
0349         if (action) {
0350             if (action->isCheckable())
0351                 m_mainWindow->handleContextDependentToggleAction(action->isChecked(), action, sessionId);
0352             else
0353                 m_mainWindow->handleContextDependentAction(action, sessionId);
0354         }
0355 
0356         m_mainWindow->setContextDependentActionsQuiet(false);
0357 
0358         updateMoveActions(m_tabs.indexOf(m_selectedSessionId));
0359         updateToggleActions(m_selectedSessionId);
0360         updateToggleKeyboardInputMenu(m_selectedSessionId);
0361         updateToggleMonitorActivityMenu(m_selectedSessionId);
0362         updateToggleMonitorSilenceMenu(m_selectedSessionId);
0363     }
0364 
0365     QWidget::contextMenuEvent(event);
0366 }
0367 
0368 void TabBar::resizeEvent(QResizeEvent *event)
0369 {
0370     moveNewTabButton();
0371     m_closeTabButton->move(width() - m_skin->tabBarCloseTabButtonPosition().x(), m_skin->tabBarCloseTabButtonPosition().y());
0372     QWidget::resizeEvent(event);
0373 }
0374 
0375 void TabBar::moveNewTabButton()
0376 {
0377     int newTabButtonX = m_skin->tabBarNewTabButtonPosition().x();
0378     if (m_skin->tabBarNewTabButtonIsAtEndOfTabs()) {
0379         newTabButtonX += m_tabWidths.last();
0380     }
0381     m_newTabButton->move(newTabButtonX, m_skin->tabBarNewTabButtonPosition().y());
0382 }
0383 
0384 void TabBar::paintEvent(QPaintEvent *)
0385 {
0386     QPainter painter(this);
0387     painter.setPen(m_skin->tabBarTextColor());
0388 
0389     int x = m_skin->tabBarPosition().x();
0390     int y = m_skin->tabBarPosition().y();
0391     m_tabWidths.clear();
0392 
0393     QRect tabsClipRect(x, y, m_closeTabButton->x() - x, height() - y);
0394     painter.setClipRect(tabsClipRect);
0395 
0396     for (int index = 0; index < m_tabs.count(); ++index) {
0397         x = drawTab(x, y, index, painter);
0398         m_tabWidths << x;
0399     }
0400 
0401     const QPixmap &backgroundImage = m_skin->tabBarBackgroundImage();
0402     const QPixmap &leftCornerImage = m_skin->tabBarLeftCornerImage();
0403     const QPixmap &rightCornerImage = m_skin->tabBarRightCornerImage();
0404 
0405     x = x > tabsClipRect.right() ? tabsClipRect.right() + 1 : x;
0406 
0407     QRegion backgroundClipRegion(rect());
0408     backgroundClipRegion = backgroundClipRegion.subtracted(m_newTabButton->geometry());
0409     backgroundClipRegion = backgroundClipRegion.subtracted(m_closeTabButton->geometry());
0410     QRect tabsRect(m_skin->tabBarPosition().x(), y, x - m_skin->tabBarPosition().x(), height() - m_skin->tabBarPosition().y());
0411     backgroundClipRegion = backgroundClipRegion.subtracted(tabsRect);
0412     painter.setClipRegion(backgroundClipRegion);
0413 
0414     painter.drawImage(0, 0, leftCornerImage.toImage());
0415     QRect leftCornerImageRect(0, 0, leftCornerImage.width(), height());
0416     backgroundClipRegion = backgroundClipRegion.subtracted(leftCornerImageRect);
0417 
0418     painter.drawImage(width() - rightCornerImage.width(), 0, rightCornerImage.toImage());
0419     QRect rightCornerImageRect(width() - rightCornerImage.width(), 0, rightCornerImage.width(), height());
0420     backgroundClipRegion = backgroundClipRegion.subtracted(rightCornerImageRect);
0421 
0422     painter.setClipRegion(backgroundClipRegion);
0423 
0424     painter.drawTiledPixmap(0, 0, width(), height(), backgroundImage);
0425 
0426     painter.end();
0427 
0428     if (m_skin->tabBarNewTabButtonIsAtEndOfTabs()) {
0429         moveNewTabButton();
0430     }
0431 }
0432 
0433 int TabBar::drawTab(int x, int y, int index, QPainter &painter)
0434 {
0435     QString title;
0436     int sessionId;
0437     bool selected;
0438     QFont font = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
0439     int textWidth = 0;
0440 
0441     sessionId = m_tabs.at(index);
0442     selected = (sessionId == m_selectedSessionId);
0443     title = m_tabTitles[sessionId];
0444 
0445     if (selected) {
0446         painter.drawPixmap(x, y, m_skin->tabBarSelectedLeftCornerImage());
0447         x += m_skin->tabBarSelectedLeftCornerImage().width();
0448     } else if (!m_skin->tabBarUnselectedLeftCornerImage().isNull()) {
0449         painter.drawPixmap(x, y, m_skin->tabBarUnselectedLeftCornerImage());
0450         x += m_skin->tabBarUnselectedLeftCornerImage().width();
0451     } else if (index != m_tabs.indexOf(m_selectedSessionId) + 1) {
0452         painter.drawPixmap(x, y, m_skin->tabBarSeparatorImage());
0453         x += m_skin->tabBarSeparatorImage().width();
0454     }
0455 
0456     if (selected)
0457         font.setBold(m_skin->tabBarSelectedTextBold());
0458     else
0459         font.setBold(false);
0460 
0461     painter.setFont(font);
0462 
0463     QFontMetrics fontMetrics(font);
0464     textWidth = fontMetrics.horizontalAdvance(title) + 10;
0465 
0466     // Draw the Prevent Closing image in the tab button.
0467     if (m_mainWindow->sessionStack()->isSessionClosable(sessionId) == false) {
0468         if (selected)
0469             painter.drawTiledPixmap(x,
0470                                     y,
0471                                     m_skin->tabBarPreventClosingImagePosition().x() + m_skin->tabBarPreventClosingImage().width(),
0472                                     height(),
0473                                     m_skin->tabBarSelectedBackgroundImage());
0474         else
0475             painter.drawTiledPixmap(x,
0476                                     y,
0477                                     m_skin->tabBarPreventClosingImagePosition().x() + m_skin->tabBarPreventClosingImage().width(),
0478                                     height(),
0479                                     m_skin->tabBarUnselectedBackgroundImage());
0480 
0481         painter.drawPixmap(x + m_skin->tabBarPreventClosingImagePosition().x(),
0482                            m_skin->tabBarPreventClosingImagePosition().y(),
0483                            m_skin->tabBarPreventClosingImage());
0484 
0485         x += m_skin->tabBarPreventClosingImagePosition().x();
0486         x += m_skin->tabBarPreventClosingImage().width();
0487     }
0488 
0489     if (selected)
0490         painter.drawTiledPixmap(x, y, textWidth, height(), m_skin->tabBarSelectedBackgroundImage());
0491     else
0492         painter.drawTiledPixmap(x, y, textWidth, height(), m_skin->tabBarUnselectedBackgroundImage());
0493 
0494     painter.drawText(x, y, textWidth + 1, height() + 2, Qt::AlignHCenter | Qt::AlignVCenter, title);
0495 
0496     x += textWidth;
0497 
0498     if (selected) {
0499         painter.drawPixmap(x, m_skin->tabBarPosition().y(), m_skin->tabBarSelectedRightCornerImage());
0500         x += m_skin->tabBarSelectedRightCornerImage().width();
0501     } else if (!m_skin->tabBarUnselectedRightCornerImage().isNull()) {
0502         painter.drawPixmap(x, m_skin->tabBarPosition().y(), m_skin->tabBarUnselectedRightCornerImage());
0503         x += m_skin->tabBarUnselectedRightCornerImage().width();
0504     } else if (index != m_tabs.indexOf(m_selectedSessionId) - 1) {
0505         painter.drawPixmap(x, m_skin->tabBarPosition().y(), m_skin->tabBarSeparatorImage());
0506         x += m_skin->tabBarSeparatorImage().width();
0507     }
0508 
0509     return x;
0510 }
0511 
0512 int TabBar::tabAt(int x)
0513 {
0514     for (int index = 0; index < m_tabWidths.count(); ++index) {
0515         if (x > m_skin->tabBarPosition().x() && x < m_tabWidths.at(index))
0516             return index;
0517     }
0518 
0519     return -1;
0520 }
0521 
0522 void TabBar::wheelEvent(QWheelEvent *event)
0523 {
0524     if (event->angleDelta().y() < 0)
0525         selectNextTab();
0526     else
0527         selectPreviousTab();
0528 }
0529 
0530 void TabBar::keyPressEvent(QKeyEvent *event)
0531 {
0532     if (event->key() == Qt::Key_Escape && m_lineEdit->isVisible())
0533         m_lineEdit->hide();
0534 
0535     QWidget::keyPressEvent(event);
0536 }
0537 
0538 void TabBar::mousePressEvent(QMouseEvent *event)
0539 {
0540     if (QWhatsThis::inWhatsThisMode())
0541         return;
0542 
0543     if (event->x() < m_skin->tabBarPosition().x())
0544         return;
0545 
0546     int index = tabAt(event->x());
0547 
0548     if (index == -1)
0549         return;
0550 
0551     if (event->button() == Qt::LeftButton || event->button() == Qt::MiddleButton) {
0552         m_startPos = event->pos();
0553         if (index != m_tabs.indexOf(m_selectedSessionId) || event->button() == Qt::MiddleButton) {
0554             m_mousePressed = true;
0555             m_mousePressedIndex = index;
0556         }
0557         return;
0558     }
0559 
0560     QWidget::mousePressEvent(event);
0561 }
0562 
0563 void TabBar::mouseReleaseEvent(QMouseEvent *event)
0564 {
0565     if (QWhatsThis::inWhatsThisMode())
0566         return;
0567 
0568     if (event->x() < m_skin->tabBarPosition().x())
0569         return;
0570 
0571     int index = tabAt(event->x());
0572 
0573     if (m_mousePressed && m_mousePressedIndex == index) {
0574         if (event->button() == Qt::LeftButton && index != m_tabs.indexOf(m_selectedSessionId))
0575             Q_EMIT tabSelected(m_tabs.at(index));
0576 
0577         if (event->button() == Qt::MiddleButton)
0578             Q_EMIT tabClosed(m_tabs.at(index));
0579     }
0580 
0581     m_mousePressed = false;
0582 
0583     m_startPos.setX(0);
0584     m_startPos.setY(0);
0585 
0586     QWidget::mouseReleaseEvent(event);
0587 }
0588 
0589 void TabBar::mouseMoveEvent(QMouseEvent *event)
0590 {
0591     if (!m_startPos.isNull() && ((event->buttons() & Qt::LeftButton) || (event->buttons() & Qt::MiddleButton))) {
0592         int distance = (event->pos() - m_startPos).manhattanLength();
0593 
0594         if (distance >= QApplication::startDragDistance()) {
0595             int index = tabAt(m_startPos.x());
0596 
0597             if (index >= 0 && !m_lineEdit->isVisible())
0598                 startDrag(index);
0599         }
0600     }
0601 
0602     QWidget::mouseMoveEvent(event);
0603 }
0604 
0605 void TabBar::dragEnterEvent(QDragEnterEvent *event)
0606 {
0607     TabBar *eventSource = qobject_cast<TabBar *>(event->source());
0608 
0609     if (eventSource) {
0610         event->setDropAction(Qt::MoveAction);
0611         event->acceptProposedAction();
0612     } else {
0613         drawDropIndicator(-1);
0614         event->ignore();
0615     }
0616 
0617     return;
0618 }
0619 
0620 void TabBar::dragMoveEvent(QDragMoveEvent *event)
0621 {
0622     TabBar *eventSource = qobject_cast<TabBar *>(event->source());
0623 
0624     if (eventSource && event->pos().x() > m_skin->tabBarPosition().x() && event->pos().x() < m_closeTabButton->x()) {
0625         int index = dropIndex(event->pos());
0626 
0627         if (index == -1)
0628             index = m_tabs.count();
0629 
0630         drawDropIndicator(index, isSameTab(event));
0631 
0632         event->setDropAction(Qt::MoveAction);
0633         event->accept();
0634     } else {
0635         drawDropIndicator(-1);
0636         event->ignore();
0637     }
0638 
0639     return;
0640 }
0641 
0642 void TabBar::dragLeaveEvent(QDragLeaveEvent *event)
0643 {
0644     drawDropIndicator(-1);
0645     event->ignore();
0646 
0647     return;
0648 }
0649 
0650 void TabBar::dropEvent(QDropEvent *event)
0651 {
0652     drawDropIndicator(-1);
0653 
0654     int x = event->pos().x();
0655 
0656     if (isSameTab(event) || x < m_skin->tabBarPosition().x() || x > m_closeTabButton->x())
0657         event->ignore();
0658     else {
0659         int targetIndex = dropIndex(event->pos());
0660         int sourceSessionId = event->mimeData()->text().toInt();
0661         int sourceIndex = m_tabs.indexOf(sourceSessionId);
0662 
0663         if (targetIndex == -1)
0664             targetIndex = m_tabs.count() - 1;
0665         else if (targetIndex < 0)
0666             targetIndex = 0;
0667         else if (sourceIndex < targetIndex)
0668             --targetIndex;
0669 
0670         m_tabs.move(sourceIndex, targetIndex);
0671         Q_EMIT tabSelected(m_tabs.at(targetIndex));
0672 
0673         event->accept();
0674     }
0675 
0676     return;
0677 }
0678 
0679 void TabBar::mouseDoubleClickEvent(QMouseEvent *event)
0680 {
0681     if (QWhatsThis::inWhatsThisMode())
0682         return;
0683 
0684     m_lineEdit->hide();
0685 
0686     if (event->x() < 0)
0687         return;
0688 
0689     int index = tabAt(event->x());
0690 
0691     if (event->button() == Qt::LeftButton) {
0692         if (event->x() <= m_tabWidths.last())
0693             interactiveRename(m_tabs.at(index));
0694         else if (event->x() > m_tabWidths.last())
0695             Q_EMIT newTabRequested();
0696     }
0697 
0698     QWidget::mouseDoubleClickEvent(event);
0699 }
0700 
0701 void TabBar::leaveEvent(QEvent *event)
0702 {
0703     m_mousePressed = false;
0704     drawDropIndicator(-1);
0705     event->ignore();
0706 
0707     QWidget::leaveEvent(event);
0708 }
0709 
0710 void TabBar::addTab(int sessionId, const QString &title)
0711 {
0712     m_tabs.append(sessionId);
0713 
0714     if (title.isEmpty())
0715         m_tabTitles.insert(sessionId, standardTabTitle());
0716     else
0717         m_tabTitles.insert(sessionId, title);
0718 
0719     Q_EMIT tabSelected(sessionId);
0720 }
0721 
0722 void TabBar::removeTab(int sessionId)
0723 {
0724     if (sessionId == -1)
0725         sessionId = m_selectedSessionId;
0726     if (sessionId == -1)
0727         return;
0728     if (!m_tabs.contains(sessionId))
0729         return;
0730 
0731     int index = m_tabs.indexOf(sessionId);
0732 
0733     if (m_lineEdit->isVisible() && sessionId == m_renamingSessionId)
0734         m_lineEdit->hide();
0735 
0736     m_tabs.removeAt(index);
0737     m_tabTitles.remove(sessionId);
0738 
0739     if (m_tabs.isEmpty())
0740         Q_EMIT lastTabClosed();
0741     else if (sessionId == m_selectedSessionId)
0742         Q_EMIT tabSelected(m_tabs.last());
0743     else
0744         Q_EMIT tabSelected(m_selectedSessionId);
0745 }
0746 
0747 void TabBar::interactiveRename(int sessionId)
0748 {
0749     if (sessionId == -1)
0750         return;
0751     if (!m_tabs.contains(sessionId))
0752         return;
0753 
0754     m_renamingSessionId = sessionId;
0755 
0756     int index = m_tabs.indexOf(sessionId);
0757     int x = index ? m_tabWidths.at(index - 1) : m_skin->tabBarPosition().x();
0758     int y = m_skin->tabBarPosition().y();
0759     int width = m_tabWidths.at(index) - x;
0760 
0761     m_lineEdit->setText(m_tabTitles[sessionId]);
0762     m_lineEdit->setGeometry(x - 1, y - 1, width + 3, height() + 2);
0763     m_lineEdit->selectAll();
0764     m_lineEdit->setFocus();
0765     m_lineEdit->show();
0766 }
0767 
0768 void TabBar::interactiveRenameDone()
0769 {
0770     int sessionId = m_renamingSessionId;
0771 
0772     m_renamingSessionId = -1;
0773 
0774     setTabTitle(sessionId, m_lineEdit->text().trimmed());
0775 }
0776 
0777 void TabBar::selectTab(int sessionId)
0778 {
0779     if (!m_tabs.contains(sessionId))
0780         return;
0781 
0782     m_selectedSessionId = sessionId;
0783 
0784     updateMoveActions(m_tabs.indexOf(sessionId));
0785     updateToggleActions(sessionId);
0786 
0787     repaint();
0788 }
0789 
0790 void TabBar::selectNextTab()
0791 {
0792     int index = m_tabs.indexOf(m_selectedSessionId);
0793     int newSelectedSessionId = m_selectedSessionId;
0794 
0795     if (index == -1)
0796         return;
0797     else if (index == m_tabs.count() - 1)
0798         newSelectedSessionId = m_tabs.at(0);
0799     else
0800         newSelectedSessionId = m_tabs.at(index + 1);
0801 
0802     Q_EMIT tabSelected(newSelectedSessionId);
0803 }
0804 
0805 void TabBar::selectPreviousTab()
0806 {
0807     int index = m_tabs.indexOf(m_selectedSessionId);
0808     int newSelectedSessionId = m_selectedSessionId;
0809 
0810     if (index == -1)
0811         return;
0812     else if (index == 0)
0813         newSelectedSessionId = m_tabs.at(m_tabs.count() - 1);
0814     else
0815         newSelectedSessionId = m_tabs.at(index - 1);
0816 
0817     Q_EMIT tabSelected(newSelectedSessionId);
0818 }
0819 
0820 void TabBar::moveTabLeft(int sessionId)
0821 {
0822     if (sessionId == -1)
0823         sessionId = m_selectedSessionId;
0824 
0825     int index = m_tabs.indexOf(sessionId);
0826 
0827     if (index < 1)
0828         return;
0829 
0830     m_tabs.swapItemsAt(index, index - 1);
0831 
0832     repaint();
0833 
0834     updateMoveActions(index - 1);
0835 }
0836 
0837 void TabBar::moveTabRight(int sessionId)
0838 {
0839     if (sessionId == -1)
0840         sessionId = m_selectedSessionId;
0841 
0842     int index = m_tabs.indexOf(sessionId);
0843 
0844     if (index == -1 || index == m_tabs.count() - 1)
0845         return;
0846 
0847     m_tabs.swapItemsAt(index, index + 1);
0848 
0849     repaint();
0850 
0851     updateMoveActions(index + 1);
0852 }
0853 
0854 void TabBar::closeTabButtonClicked()
0855 {
0856     Q_EMIT tabClosed(m_selectedSessionId);
0857 }
0858 
0859 QString TabBar::tabTitle(int sessionId)
0860 {
0861     if (m_tabTitles.contains(sessionId))
0862         return m_tabTitles[sessionId];
0863     else
0864         return QString();
0865 }
0866 
0867 void TabBar::setTabTitle(int sessionId, const QString &newTitle, InteractiveType interactive)
0868 {
0869     if (sessionId == -1)
0870         return;
0871     if (!m_tabTitles.contains(sessionId))
0872         return;
0873     if (!interactive && m_tabTitlesSetInteractive.value(sessionId, false))
0874         return;
0875     if (interactive)
0876         m_tabTitlesSetInteractive[sessionId] = interactive;
0877 
0878     if (!newTitle.isEmpty()) {
0879         m_tabTitles[sessionId] = newTitle;
0880     } else
0881         m_tabTitlesSetInteractive.remove(sessionId);
0882 
0883     Q_EMIT tabTitleEdited(sessionId, newTitle);
0884     update();
0885 }
0886 
0887 void TabBar::setTabTitleAutomated(int sessionId, const QString &newTitle)
0888 {
0889     setTabTitle(sessionId, newTitle, NonInteractive);
0890 }
0891 
0892 int TabBar::sessionAtTab(int index)
0893 {
0894     if (index < 0 || index > m_tabs.count() - 1)
0895         return -1;
0896     else
0897         return m_tabs.at(index);
0898 }
0899 
0900 QString TabBar::standardTabTitle()
0901 {
0902     QString newTitle = makeTabTitle(0);
0903 
0904     bool nameOk;
0905     int count = 0;
0906 
0907     do {
0908         nameOk = true;
0909 
0910         QHashIterator<int, QString> it(m_tabTitles);
0911 
0912         while (it.hasNext()) {
0913             it.next();
0914 
0915             if (newTitle == it.value()) {
0916                 nameOk = false;
0917                 break;
0918             }
0919         }
0920 
0921         if (!nameOk) {
0922             count++;
0923             newTitle = makeTabTitle(count);
0924         }
0925     } while (!nameOk);
0926 
0927     return newTitle;
0928 }
0929 
0930 QString TabBar::makeTabTitle(int id)
0931 {
0932     if (id == 0) {
0933         return xi18nc("@title:tab", "Shell");
0934     } else {
0935         return xi18nc("@title:tab", "Shell No. %1", id + 1);
0936     }
0937 }
0938 
0939 void TabBar::startDrag(int index)
0940 {
0941     int sessionId = sessionAtTab(index);
0942 
0943     m_startPos.setX(0);
0944     m_startPos.setY(0);
0945 
0946     int x = index ? m_tabWidths.at(index - 1) : m_skin->tabBarPosition().x();
0947     int tabWidth = m_tabWidths.at(index) - x;
0948 
0949     QPixmap tab(tabWidth, height());
0950     QColor fillColor(Settings::backgroundColor());
0951 
0952     if (m_mainWindow->useTranslucency())
0953         fillColor.setAlphaF(qreal(Settings::backgroundColorOpacity()) / 100);
0954 
0955     tab.fill(fillColor);
0956 
0957     QPainter painter(&tab);
0958     painter.begin(this);
0959     painter.setPen(m_skin->tabBarTextColor());
0960 
0961     drawTab(0, 0, index, painter);
0962     painter.end();
0963 
0964     QMimeData *mimeData = new QMimeData;
0965     mimeData->setText(QVariant(sessionId).toString());
0966 
0967     QDrag *drag = new QDrag(this);
0968     drag->setMimeData(mimeData);
0969     drag->setPixmap(tab);
0970     drag->exec(Qt::MoveAction);
0971 
0972     return;
0973 }
0974 
0975 void TabBar::drawDropIndicator(int index, bool disabled)
0976 {
0977     const int arrowSize = 16;
0978 
0979     if (!m_dropIndicator) {
0980         m_dropIndicator = new QLabel(parentWidget());
0981         m_dropIndicator->resize(arrowSize, arrowSize);
0982     }
0983 
0984     QIcon::Mode drawMode = disabled ? QIcon::Disabled : QIcon::Normal;
0985     m_dropIndicator->setPixmap(QIcon::fromTheme(QStringLiteral("arrow-down")).pixmap(arrowSize, arrowSize, drawMode));
0986 
0987     if (index < 0) {
0988         m_dropIndicator->hide();
0989         return;
0990     }
0991 
0992     int temp_index;
0993     if (index == m_tabs.count())
0994         temp_index = index - 1;
0995     else
0996         temp_index = index;
0997 
0998     int x = temp_index ? m_tabWidths.at(temp_index - 1) : m_skin->tabBarPosition().x();
0999     int tabWidth = m_tabWidths.at(temp_index) - x;
1000     int y = m_skin->tabBarPosition().y();
1001 
1002     m_dropRect = QRect(x, y - height(), tabWidth, height() - y);
1003     QPoint pos;
1004 
1005     if (index < m_tabs.count())
1006         pos = m_dropRect.topLeft();
1007     else
1008         pos = m_dropRect.topRight();
1009 
1010     pos.rx() -= arrowSize / 2;
1011 
1012     m_dropIndicator->move(mapTo(parentWidget(), pos));
1013     m_dropIndicator->show();
1014 
1015     return;
1016 }
1017 
1018 int TabBar::dropIndex(const QPoint pos)
1019 {
1020     int index = tabAt(pos.x());
1021     if (index < 0)
1022         return index;
1023 
1024     int x = index ? m_tabWidths.at(index - 1) : m_skin->tabBarPosition().x();
1025     int tabWidth = m_tabWidths.at(index) - x;
1026     int y = m_skin->tabBarPosition().y();
1027     m_dropRect = QRect(x, y - height(), tabWidth, height() - y);
1028 
1029     if ((pos.x() - m_dropRect.left()) > (m_dropRect.width() / 2))
1030         ++index;
1031 
1032     if (index == m_tabs.count())
1033         return -1;
1034 
1035     return index;
1036 }
1037 
1038 bool TabBar::isSameTab(const QDropEvent *event)
1039 {
1040     int index = dropIndex(event->pos());
1041     int sourceSessionId = event->mimeData()->text().toInt();
1042     int sourceIndex = m_tabs.indexOf(sourceSessionId);
1043 
1044     bool isLastTab = (sourceIndex == m_tabs.count() - 1) && (index == -1);
1045 
1046     if ((sourceIndex == index) || (sourceIndex == index - 1) || isLastTab)
1047         return true;
1048     else
1049         return false;
1050 }
1051 
1052 #include "moc_tabbar.cpp"