mirror of
https://github.com/KDE/konsole.git
synced 2025-12-23 23:38:08 -05:00
786 lines
27 KiB
C++
786 lines
27 KiB
C++
/*
|
|
SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
// Own
|
|
#include "widgets/ViewContainer.h"
|
|
#include "config-konsole.h"
|
|
|
|
// Qt
|
|
#include <QBoxLayout>
|
|
#include <QFile>
|
|
#include <QKeyEvent>
|
|
#include <QMenu>
|
|
#include <QTabBar>
|
|
|
|
// KDE
|
|
#include <KActionCollection>
|
|
#include <KColorScheme>
|
|
#include <KColorUtils>
|
|
#include <KLocalizedString>
|
|
|
|
// Konsole
|
|
#include "DetachableTabBar.h"
|
|
#include "KonsoleSettings.h"
|
|
#include "ViewProperties.h"
|
|
#include "profile/ProfileList.h"
|
|
#include "searchtabs/SearchTabs.h"
|
|
#include "session/SessionController.h"
|
|
#include "session/SessionManager.h"
|
|
#include "terminalDisplay/TerminalDisplay.h"
|
|
#include "widgets/IncrementalSearchBar.h"
|
|
#include "widgets/ViewSplitter.h"
|
|
|
|
// TODO Perhaps move everything which is Konsole-specific into different files
|
|
|
|
using namespace Konsole;
|
|
|
|
TabbedViewContainer::TabbedViewContainer(ViewManager *connectedViewManager, QWidget *parent)
|
|
: QTabWidget(parent)
|
|
, _connectedViewManager(connectedViewManager)
|
|
, _newTabButton(new QToolButton(this))
|
|
, _searchTabsButton(new QToolButton(this))
|
|
, _closeTabButton(new QToolButton(this))
|
|
, _contextMenuTabIndex(-1)
|
|
, _newTabBehavior(PutNewTabAtTheEnd)
|
|
{
|
|
setAcceptDrops(true);
|
|
|
|
auto tabBarWidget = new DetachableTabBar(this);
|
|
setTabBar(tabBarWidget);
|
|
setDocumentMode(true);
|
|
setMovable(true);
|
|
connect(tabBarWidget, &DetachableTabBar::moveTabToWindow, this, &TabbedViewContainer::moveTabToWindow);
|
|
tabBar()->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
_newTabButton->setIcon(QIcon::fromTheme(QStringLiteral("tab-new")));
|
|
_newTabButton->setAutoRaise(true);
|
|
_newTabButton->setToolTip(i18nc("@info:tooltip", "Open a new tab"));
|
|
connect(_newTabButton, &QToolButton::clicked, this, &TabbedViewContainer::newViewRequest);
|
|
|
|
_searchTabsButton->setIcon(QIcon::fromTheme(QStringLiteral("quickopen")));
|
|
_searchTabsButton->setAutoRaise(true);
|
|
_searchTabsButton->setToolTip(i18nc("@info:tooltip", "Search Tabs"));
|
|
connect(_searchTabsButton, &QToolButton::clicked, this, &TabbedViewContainer::searchTabs);
|
|
|
|
_closeTabButton->setIcon(QIcon::fromTheme(QStringLiteral("tab-close")));
|
|
_closeTabButton->setAutoRaise(true);
|
|
_closeTabButton->setToolTip(i18nc("@info:tooltip", "Close this tab"));
|
|
connect(_closeTabButton, &QToolButton::clicked, this, [this] {
|
|
closeCurrentTab();
|
|
});
|
|
|
|
connect(tabBar(), &QTabBar::tabBarDoubleClicked, this, &Konsole::TabbedViewContainer::tabDoubleClicked);
|
|
connect(tabBar(), &QTabBar::customContextMenuRequested, this, &Konsole::TabbedViewContainer::openTabContextMenu);
|
|
connect(tabBarWidget, &DetachableTabBar::detachTab, this, [this](int idx) {
|
|
Q_EMIT detachTab(idx);
|
|
});
|
|
connect(tabBarWidget, &DetachableTabBar::closeTab, this, &TabbedViewContainer::closeTerminalTab);
|
|
connect(tabBarWidget, &DetachableTabBar::newTabRequest, this, [this] {
|
|
Q_EMIT newViewRequest();
|
|
});
|
|
connect(this, &TabbedViewContainer::currentChanged, this, &TabbedViewContainer::currentTabChanged);
|
|
|
|
connect(this, &TabbedViewContainer::setColor, tabBarWidget, &DetachableTabBar::setColor);
|
|
connect(this, &TabbedViewContainer::removeColor, tabBarWidget, &DetachableTabBar::removeColor);
|
|
|
|
// The context menu of tab bar
|
|
_contextPopupMenu = new QMenu(tabBar());
|
|
connect(_contextPopupMenu, &QMenu::aboutToHide, this, [this]() {
|
|
// Remove the read-only action when the popup closes
|
|
for (auto &action : _contextPopupMenu->actions()) {
|
|
if (action->objectName() == QStringLiteral("view-readonly")) {
|
|
_contextPopupMenu->removeAction(action);
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
connect(tabBar(), &QTabBar::tabCloseRequested, this, &TabbedViewContainer::closeTerminalTab);
|
|
|
|
auto detachAction = _contextPopupMenu->addAction(QIcon::fromTheme(QStringLiteral("tab-detach")), i18nc("@action:inmenu", "&Detach Tab"), this, [this] {
|
|
Q_EMIT detachTab(_contextMenuTabIndex);
|
|
});
|
|
detachAction->setObjectName(QStringLiteral("tab-detach"));
|
|
|
|
auto editAction =
|
|
_contextPopupMenu->addAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18nc("@action:inmenu", "&Configure or Rename Tab..."), this, [this] {
|
|
renameTab(_contextMenuTabIndex);
|
|
});
|
|
editAction->setObjectName(QStringLiteral("edit-rename"));
|
|
|
|
auto closeAction = _contextPopupMenu->addAction(QIcon::fromTheme(QStringLiteral("tab-close")), i18nc("@action:inmenu", "Close Tab"), this, [this] {
|
|
closeTerminalTab(_contextMenuTabIndex);
|
|
});
|
|
closeAction->setObjectName(QStringLiteral("tab-close"));
|
|
|
|
auto profileMenu = new QMenu(this);
|
|
auto profileList = new ProfileList(false, profileMenu);
|
|
profileList->syncWidgetActions(profileMenu, true);
|
|
connect(profileList, &Konsole::ProfileList::profileSelected, this, &TabbedViewContainer::newViewWithProfileRequest);
|
|
_newTabButton->setMenu(profileMenu);
|
|
|
|
konsoleConfigChanged();
|
|
connect(KonsoleSettings::self(), &KonsoleSettings::configChanged, this, &TabbedViewContainer::konsoleConfigChanged);
|
|
}
|
|
|
|
TabbedViewContainer::~TabbedViewContainer()
|
|
{
|
|
for (int i = 0, end = count(); i < end; i++) {
|
|
auto view = widget(i);
|
|
disconnect(view, &QWidget::destroyed, this, &Konsole::TabbedViewContainer::viewDestroyed);
|
|
}
|
|
}
|
|
|
|
ViewSplitter *TabbedViewContainer::activeViewSplitter()
|
|
{
|
|
return viewSplitterAt(currentIndex());
|
|
}
|
|
|
|
ViewSplitter *TabbedViewContainer::viewSplitterAt(int index)
|
|
{
|
|
return qobject_cast<ViewSplitter *>(widget(index));
|
|
}
|
|
|
|
ViewSplitter *TabbedViewContainer::findSplitter(int id)
|
|
{
|
|
for (int i = 0; i < count(); ++i) {
|
|
auto toplevelSplitter = viewSplitterAt(i);
|
|
|
|
if (toplevelSplitter->id() == id)
|
|
return toplevelSplitter;
|
|
|
|
if (auto result = toplevelSplitter->getChildSplitter(id))
|
|
return result;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
int TabbedViewContainer::currentTabViewCount()
|
|
{
|
|
if (auto *splitter = activeViewSplitter()) {
|
|
return splitter->findChildren<TerminalDisplay *>().count();
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void TabbedViewContainer::moveTabToWindow(int index, QWidget *window)
|
|
{
|
|
auto splitter = viewSplitterAt(index);
|
|
auto manager = window->findChild<ViewManager *>();
|
|
|
|
QHash<TerminalDisplay *, Session *> sessionsMap = _connectedViewManager->forgetAll(splitter);
|
|
|
|
const QList<TerminalDisplay *> displays = splitter->findChildren<TerminalDisplay *>();
|
|
for (TerminalDisplay *terminal : displays) {
|
|
manager->attachView(terminal, sessionsMap[terminal]);
|
|
}
|
|
auto container = manager->activeContainer();
|
|
container->addSplitter(splitter);
|
|
|
|
auto controller = splitter->activeTerminalDisplay()->sessionController();
|
|
container->currentSessionControllerChanged(controller);
|
|
|
|
forgetView();
|
|
}
|
|
|
|
void TabbedViewContainer::konsoleConfigChanged()
|
|
{
|
|
// don't show tabs if we are in KParts mode.
|
|
// This is a hack, and this needs to be rewritten.
|
|
// The container should not be part of the KParts, perhaps just the
|
|
// TerminalDisplay should.
|
|
|
|
// ASAN issue if using sessionController->isKonsolePart(), just
|
|
// duplicate code for now
|
|
if (qApp->applicationName() != QLatin1String("konsole")) {
|
|
tabBar()->setVisible(false);
|
|
} else {
|
|
// if we start with --show-tabbar or --hide-tabbar we ignore the preferences.
|
|
setTabBarAutoHide(KonsoleSettings::tabBarVisibility() == KonsoleSettings::EnumTabBarVisibility::ShowTabBarWhenNeeded);
|
|
if (KonsoleSettings::tabBarVisibility() == KonsoleSettings::EnumTabBarVisibility::AlwaysShowTabBar) {
|
|
tabBar()->setVisible(true);
|
|
} else if (KonsoleSettings::tabBarVisibility() == KonsoleSettings::EnumTabBarVisibility::AlwaysHideTabBar) {
|
|
tabBar()->setVisible(false);
|
|
}
|
|
}
|
|
|
|
setTabPosition((QTabWidget::TabPosition)KonsoleSettings::tabBarPosition());
|
|
|
|
setCornerWidget(KonsoleSettings::newTabButton() ? _newTabButton : nullptr, Qt::TopLeftCorner);
|
|
_newTabButton->setVisible(KonsoleSettings::newTabButton());
|
|
|
|
// Add Layout for right corner tool buttons
|
|
auto layout = new QHBoxLayout();
|
|
layout->setStretch(0, 10);
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
layout->setSpacing(0);
|
|
|
|
if (KonsoleSettings::searchTabsButton() == 0) {
|
|
layout->addWidget(_searchTabsButton);
|
|
}
|
|
_searchTabsButton->setVisible(KonsoleSettings::searchTabsButton() == 0);
|
|
|
|
if (KonsoleSettings::closeTabButton() == 1) {
|
|
layout->addWidget(_closeTabButton);
|
|
}
|
|
_closeTabButton->setVisible(KonsoleSettings::closeTabButton() == 1);
|
|
|
|
QWidget *rightCornerWidget = new QWidget();
|
|
rightCornerWidget->setLayout(layout);
|
|
setCornerWidget(rightCornerWidget, Qt::TopRightCorner);
|
|
rightCornerWidget->setVisible(true);
|
|
|
|
tabBar()->setTabsClosable(KonsoleSettings::closeTabButton() == 0);
|
|
|
|
tabBar()->setExpanding(KonsoleSettings::expandTabWidth());
|
|
tabBar()->update();
|
|
|
|
if (KonsoleSettings::tabBarUseUserStyleSheet()) {
|
|
setCssFromFile(KonsoleSettings::tabBarUserStyleSheetFile());
|
|
_stylesheetSet = true;
|
|
} else {
|
|
if (_stylesheetSet) {
|
|
setStyleSheet(QString());
|
|
_stylesheetSet = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::setCssFromFile(const QUrl &url)
|
|
{
|
|
// Let's only deal w/ local files for now
|
|
if (!url.isLocalFile()) {
|
|
setStyleSheet(KonsoleSettings::tabBarStyleSheet());
|
|
}
|
|
|
|
QFile file(url.toLocalFile());
|
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
setStyleSheet(KonsoleSettings::tabBarStyleSheet());
|
|
}
|
|
|
|
QTextStream in(&file);
|
|
setStyleSheet(in.readAll());
|
|
}
|
|
|
|
void TabbedViewContainer::moveActiveView(MoveDirection direction)
|
|
{
|
|
if (count() < 2) { // return if only one view
|
|
return;
|
|
}
|
|
const int currentIndex = indexOf(currentWidget());
|
|
int newIndex = direction == MoveViewLeft ? qMax(currentIndex - 1, 0) : qMin(currentIndex + 1, count() - 1);
|
|
|
|
auto swappedWidget = viewSplitterAt(newIndex);
|
|
auto swappedTitle = tabBar()->tabText(newIndex);
|
|
auto swappedIcon = tabBar()->tabIcon(newIndex);
|
|
|
|
auto currentWidget = viewSplitterAt(currentIndex);
|
|
auto currentTitle = tabBar()->tabText(currentIndex);
|
|
auto currentIcon = tabBar()->tabIcon(currentIndex);
|
|
|
|
if (newIndex < currentIndex) {
|
|
insertTab(newIndex, currentWidget, currentIcon, currentTitle);
|
|
insertTab(currentIndex, swappedWidget, swappedIcon, swappedTitle);
|
|
} else {
|
|
insertTab(currentIndex, swappedWidget, swappedIcon, swappedTitle);
|
|
insertTab(newIndex, currentWidget, currentIcon, currentTitle);
|
|
}
|
|
setCurrentIndex(newIndex);
|
|
}
|
|
|
|
void TabbedViewContainer::terminalDisplayDropped(TerminalDisplay *terminalDisplay)
|
|
{
|
|
auto *controller = terminalDisplay->sessionController();
|
|
if (controller->parent() != connectedViewManager()) {
|
|
// Terminal from another window - recreate SessionController for current ViewManager
|
|
disconnectTerminalDisplay(terminalDisplay);
|
|
Session *terminalSession = controller->session();
|
|
Q_EMIT controller->viewDragAndDropped(controller);
|
|
connectedViewManager()->attachView(terminalDisplay, terminalSession);
|
|
connectTerminalDisplay(terminalDisplay);
|
|
}
|
|
}
|
|
|
|
QSize TabbedViewContainer::sizeHint() const
|
|
{
|
|
// QTabWidget::sizeHint() contains some margins added by widgets
|
|
// style, which were making the initial window size too big.
|
|
const auto tabsSize = tabBar()->sizeHint();
|
|
const auto *leftWidget = cornerWidget(Qt::TopLeftCorner);
|
|
const auto *rightWidget = cornerWidget(Qt::TopRightCorner);
|
|
const auto leftSize = leftWidget != nullptr ? leftWidget->sizeHint() : QSize(0, 0);
|
|
const auto rightSize = rightWidget != nullptr ? rightWidget->sizeHint() : QSize(0, 0);
|
|
|
|
auto tabBarSize = QSize(0, 0);
|
|
// isVisible() won't work; this is called when the window is not yet visible
|
|
if (tabBar()->isVisibleTo(this)) {
|
|
tabBarSize.setWidth(leftSize.width() + tabsSize.width() + rightSize.width());
|
|
tabBarSize.setHeight(qMax(tabsSize.height(), qMax(leftSize.height(), rightSize.height())));
|
|
}
|
|
|
|
const auto terminalSize = currentWidget() != nullptr ? currentWidget()->sizeHint() : QSize(0, 0);
|
|
|
|
// width
|
|
// ├──────────────────┤
|
|
//
|
|
// ┌──────────────────┐ ┬
|
|
// │ │ │
|
|
// │ Terminal │ │
|
|
// │ │ │ height
|
|
// ├───┬──────────┬───┤ │ ┬
|
|
// │ L │ Tabs │ R │ │ │ tab bar height
|
|
// └───┴──────────┴───┘ ┴ ┴
|
|
//
|
|
// L/R = left/right widget
|
|
|
|
return {qMax(terminalSize.width(), tabBarSize.width()), tabBarSize.height() + terminalSize.height()};
|
|
}
|
|
|
|
void TabbedViewContainer::addSplitter(ViewSplitter *viewSplitter, int index)
|
|
{
|
|
index = insertTab(index, viewSplitter, QString());
|
|
connect(viewSplitter, &ViewSplitter::destroyed, this, &TabbedViewContainer::viewDestroyed);
|
|
|
|
disconnect(viewSplitter, &ViewSplitter::terminalDisplayDropped, nullptr, nullptr);
|
|
connect(viewSplitter, &ViewSplitter::terminalDisplayDropped, this, &TabbedViewContainer::terminalDisplayDropped);
|
|
|
|
const auto terminalDisplays = viewSplitter->findChildren<TerminalDisplay *>();
|
|
for (TerminalDisplay *terminal : terminalDisplays) {
|
|
connectTerminalDisplay(terminal);
|
|
}
|
|
if (terminalDisplays.count() > 0) {
|
|
updateTitle(qobject_cast<ViewProperties *>(terminalDisplays.at(0)->sessionController()));
|
|
updateColor(qobject_cast<ViewProperties *>(terminalDisplays.at(0)->sessionController()));
|
|
}
|
|
setCurrentIndex(index);
|
|
}
|
|
|
|
void TabbedViewContainer::addView(TerminalDisplay *view)
|
|
{
|
|
auto viewSplitter = new ViewSplitter();
|
|
viewSplitter->addTerminalDisplay(view, Qt::Horizontal);
|
|
auto item = view->sessionController();
|
|
int index = _newTabBehavior == PutNewTabAfterCurrentTab ? currentIndex() + 1 : -1;
|
|
index = insertTab(index, viewSplitter, item->icon(), item->title());
|
|
|
|
connectTerminalDisplay(view);
|
|
connect(viewSplitter, &ViewSplitter::destroyed, this, &TabbedViewContainer::viewDestroyed);
|
|
connect(viewSplitter, &ViewSplitter::terminalDisplayDropped, this, &TabbedViewContainer::terminalDisplayDropped);
|
|
|
|
// Put this view on the foreground if it requests so, eg. on bell activity
|
|
connect(view, &TerminalDisplay::activationRequest, this, &Konsole::TabbedViewContainer::activateView);
|
|
|
|
setCurrentIndex(index);
|
|
Q_EMIT viewAdded(view);
|
|
}
|
|
|
|
void TabbedViewContainer::splitView(TerminalDisplay *view, Qt::Orientation orientation)
|
|
{
|
|
auto viewSplitter = qobject_cast<ViewSplitter *>(currentWidget());
|
|
viewSplitter->clearMaximized();
|
|
viewSplitter->addTerminalDisplay(view, orientation);
|
|
connectTerminalDisplay(view);
|
|
// Put this view on the foreground if it requests so, eg. on bell activity
|
|
connect(view, &TerminalDisplay::activationRequest, this, &Konsole::TabbedViewContainer::activateView);
|
|
}
|
|
|
|
void TabbedViewContainer::connectTerminalDisplay(TerminalDisplay *display)
|
|
{
|
|
auto item = display->sessionController();
|
|
connect(item, &Konsole::SessionController::viewFocused, this, &Konsole::TabbedViewContainer::currentSessionControllerChanged);
|
|
|
|
connect(item, &Konsole::ViewProperties::titleChanged, this, &Konsole::TabbedViewContainer::updateTitle);
|
|
|
|
connect(item, &Konsole::ViewProperties::colorChanged, this, &Konsole::TabbedViewContainer::updateColor);
|
|
|
|
connect(item, &Konsole::ViewProperties::iconChanged, this, &Konsole::TabbedViewContainer::updateIcon);
|
|
|
|
connect(item, &Konsole::ViewProperties::activity, this, &Konsole::TabbedViewContainer::updateActivity);
|
|
|
|
connect(item, &Konsole::ViewProperties::notificationChanged, this, &Konsole::TabbedViewContainer::updateNotification);
|
|
|
|
connect(item, &Konsole::ViewProperties::readOnlyChanged, this, &Konsole::TabbedViewContainer::updateSpecialState);
|
|
|
|
connect(item, &Konsole::ViewProperties::copyInputChanged, this, &Konsole::TabbedViewContainer::updateSpecialState);
|
|
}
|
|
|
|
void TabbedViewContainer::disconnectTerminalDisplay(TerminalDisplay *display)
|
|
{
|
|
auto item = display->sessionController();
|
|
item->disconnect(this);
|
|
}
|
|
|
|
void TabbedViewContainer::viewDestroyed(QObject *view)
|
|
{
|
|
QWidget *widget = qobject_cast<QWidget *>(view);
|
|
Q_ASSERT(widget);
|
|
const int idx = indexOf(widget);
|
|
|
|
removeTab(idx);
|
|
forgetView();
|
|
_tabIconState.remove(widget);
|
|
|
|
Q_EMIT viewRemoved();
|
|
}
|
|
|
|
void TabbedViewContainer::forgetView()
|
|
{
|
|
if (count() == 0) {
|
|
Q_EMIT empty(this);
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::activateView(const QString & /*xdgActivationToken*/)
|
|
{
|
|
if (QWidget *widget = qobject_cast<QWidget *>(sender())) {
|
|
auto topLevelSplitter = qobject_cast<ViewSplitter *>(widget->parentWidget())->getToplevelSplitter();
|
|
setCurrentWidget(topLevelSplitter);
|
|
widget->setFocus();
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::activateNextView()
|
|
{
|
|
QWidget *active = currentWidget();
|
|
int index = indexOf(active);
|
|
setCurrentIndex(index == count() - 1 ? 0 : index + 1);
|
|
}
|
|
|
|
void TabbedViewContainer::activateLastView()
|
|
{
|
|
setCurrentIndex(count() - 1);
|
|
}
|
|
|
|
void TabbedViewContainer::activatePreviousView()
|
|
{
|
|
QWidget *active = currentWidget();
|
|
int index = indexOf(active);
|
|
setCurrentIndex(index == 0 ? count() - 1 : index - 1);
|
|
}
|
|
|
|
void TabbedViewContainer::keyReleaseEvent(QKeyEvent *event)
|
|
{
|
|
if (event->modifiers() == Qt::NoModifier) {
|
|
_connectedViewManager->updateTerminalDisplayHistory();
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::closeCurrentTab()
|
|
{
|
|
if (currentIndex() != -1) {
|
|
closeTerminalTab(currentIndex());
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::tabDoubleClicked(int index)
|
|
{
|
|
if (index >= 0) {
|
|
renameTab(index);
|
|
} else {
|
|
Q_EMIT newViewRequest();
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::renameTab(int index)
|
|
{
|
|
if (index != -1) {
|
|
setCurrentIndex(index);
|
|
viewSplitterAt(index)->activeTerminalDisplay()->sessionController()->rename();
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::searchTabs()
|
|
{
|
|
/**
|
|
* show tab search and pass focus to it
|
|
*/
|
|
SearchTabs *searchTabs = new SearchTabs(this->connectedViewManager());
|
|
setFocusProxy(searchTabs);
|
|
searchTabs->raise();
|
|
searchTabs->show();
|
|
}
|
|
|
|
void TabbedViewContainer::openTabContextMenu(const QPoint &point)
|
|
{
|
|
if (point.isNull()) {
|
|
return;
|
|
}
|
|
|
|
_contextMenuTabIndex = tabBar()->tabAt(point);
|
|
if (_contextMenuTabIndex < 0) {
|
|
return;
|
|
}
|
|
|
|
// TODO: add a countChanged signal so we can remove this for.
|
|
// Detaching in mac causes crashes.
|
|
const auto actions = _contextPopupMenu->actions();
|
|
for (auto action : actions) {
|
|
if (action->objectName() == QStringLiteral("tab-detach")) {
|
|
action->setEnabled(count() > 1);
|
|
}
|
|
}
|
|
|
|
_contextPopupMenu->exec(tabBar()->mapToGlobal(point));
|
|
}
|
|
|
|
void TabbedViewContainer::currentTabChanged(int index)
|
|
{
|
|
if (index != -1) {
|
|
auto splitview = qobject_cast<ViewSplitter *>(widget(index));
|
|
auto view = splitview->activeTerminalDisplay();
|
|
setTabActivity(index, false);
|
|
_tabIconState[splitview].notification = Session::NoNotification;
|
|
if (view != nullptr) {
|
|
Q_EMIT activeViewChanged(view);
|
|
updateIcon(view->sessionController());
|
|
}
|
|
} else {
|
|
deleteLater();
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::wheelScrolled(int delta)
|
|
{
|
|
if (delta < 0) {
|
|
activateNextView();
|
|
} else {
|
|
activatePreviousView();
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::setTabActivity(int index, bool activity)
|
|
{
|
|
const QPalette &palette = tabBar()->palette();
|
|
KColorScheme colorScheme(palette.currentColorGroup());
|
|
const QColor colorSchemeActive = colorScheme.foreground(KColorScheme::ActiveText).color();
|
|
|
|
const QColor normalColor = palette.text().color();
|
|
const QColor activityColor = KColorUtils::mix(normalColor, colorSchemeActive);
|
|
|
|
QColor color = activity ? activityColor : QColor();
|
|
|
|
if (color != tabBar()->tabTextColor(index)) {
|
|
tabBar()->setTabTextColor(index, color);
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::updateTitle(ViewProperties *item)
|
|
{
|
|
auto controller = qobject_cast<SessionController *>(item);
|
|
auto topLevelSplitter = qobject_cast<ViewSplitter *>(controller->view()->parentWidget())->getToplevelSplitter();
|
|
if (controller->view() != topLevelSplitter->activeTerminalDisplay()) {
|
|
return;
|
|
}
|
|
const int index = indexOf(topLevelSplitter);
|
|
QString tabText = item->title();
|
|
|
|
setTabToolTip(index, tabText);
|
|
|
|
// To avoid having & replaced with _ (shortcut indicator)
|
|
tabText.replace(QLatin1Char('&'), QLatin1String("&&"));
|
|
setTabText(index, tabText);
|
|
}
|
|
|
|
void TabbedViewContainer::updateColor(ViewProperties *item)
|
|
{
|
|
auto controller = qobject_cast<SessionController *>(item);
|
|
auto topLevelSplitter = qobject_cast<ViewSplitter *>(controller->view()->parentWidget())->getToplevelSplitter();
|
|
const int index = indexOf(topLevelSplitter);
|
|
|
|
Q_EMIT setColor(index, item->color());
|
|
}
|
|
|
|
void TabbedViewContainer::updateIcon(ViewProperties *item)
|
|
{
|
|
auto controller = qobject_cast<SessionController *>(item);
|
|
auto topLevelSplitter = qobject_cast<ViewSplitter *>(controller->view()->parentWidget())->getToplevelSplitter();
|
|
const int index = indexOf(topLevelSplitter);
|
|
const auto &state = _tabIconState[topLevelSplitter];
|
|
|
|
// Tab icon priority (from highest to lowest):
|
|
//
|
|
// 1. Latest Notification
|
|
// - Inactive tab: Latest notification from any view in a tab. Removed
|
|
// when tab is activated.
|
|
// - Active tab: Latest notification from focused view. Removed when
|
|
// focus changes or when the Session clears its notifications
|
|
// 2. Copy input or read-only indicator when all views in the tab have
|
|
// the status
|
|
// 3. Active view icon
|
|
|
|
QIcon icon = item->icon();
|
|
if (state.notification != Session::NoNotification) {
|
|
switch (state.notification) {
|
|
case Session::Bell: {
|
|
auto session = controller->session();
|
|
auto profilePtr = SessionManager::instance()->sessionProfile(session);
|
|
if (profilePtr->property<int>(Profile::BellMode) != Enum::NoBell) {
|
|
icon = QIcon::fromTheme(QLatin1String("notifications"));
|
|
}
|
|
} break;
|
|
case Session::Activity:
|
|
icon = QIcon::fromTheme(QLatin1String("dialog-information"));
|
|
break;
|
|
case Session::Silence:
|
|
icon = QIcon::fromTheme(QLatin1String("system-suspend"));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (state.broadcast) {
|
|
icon = QIcon::fromTheme(QLatin1String("irc-voice"));
|
|
} else if (state.readOnly) {
|
|
icon = QIcon::fromTheme(QLatin1String("object-locked"));
|
|
}
|
|
|
|
if (tabIcon(index).name() != icon.name()) {
|
|
setTabIcon(index, icon);
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::updateActivity(ViewProperties *item)
|
|
{
|
|
auto controller = qobject_cast<SessionController *>(item);
|
|
auto topLevelSplitter = qobject_cast<ViewSplitter *>(controller->view()->parentWidget())->getToplevelSplitter();
|
|
|
|
const int index = indexOf(topLevelSplitter);
|
|
if (index != currentIndex()) {
|
|
setTabActivity(index, true);
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::updateNotification(ViewProperties *item, Session::Notification notification, bool enabled)
|
|
{
|
|
auto controller = qobject_cast<SessionController *>(item);
|
|
auto topLevelSplitter = qobject_cast<ViewSplitter *>(controller->view()->parentWidget())->getToplevelSplitter();
|
|
const int index = indexOf(topLevelSplitter);
|
|
auto &state = _tabIconState[topLevelSplitter];
|
|
|
|
if (enabled && (index != currentIndex() || controller->view()->hasCompositeFocus())) {
|
|
state.notification = notification;
|
|
updateIcon(item);
|
|
} else if (!enabled && controller->view()->hasCompositeFocus()) {
|
|
state.notification = Session::NoNotification;
|
|
updateIcon(item);
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::updateSpecialState(ViewProperties *item)
|
|
{
|
|
auto controller = qobject_cast<SessionController *>(item);
|
|
auto topLevelSplitter = qobject_cast<ViewSplitter *>(controller->view()->parentWidget())->getToplevelSplitter();
|
|
|
|
auto &state = _tabIconState[topLevelSplitter];
|
|
state.readOnly = true;
|
|
state.broadcast = true;
|
|
const auto displays = topLevelSplitter->findChildren<TerminalDisplay *>();
|
|
for (const auto display : displays) {
|
|
if (!display->sessionController()->isReadOnly()) {
|
|
state.readOnly = false;
|
|
}
|
|
if (!display->sessionController()->isCopyInputActive()) {
|
|
state.broadcast = false;
|
|
}
|
|
}
|
|
updateIcon(item);
|
|
}
|
|
|
|
void TabbedViewContainer::currentSessionControllerChanged(SessionController *controller)
|
|
{
|
|
auto topLevelSplitter = qobject_cast<ViewSplitter *>(controller->view()->parentWidget())->getToplevelSplitter();
|
|
const int index = indexOf(topLevelSplitter);
|
|
|
|
if (index == currentIndex()) {
|
|
// Active view changed in current tab - clear notifications
|
|
auto &state = _tabIconState[topLevelSplitter];
|
|
state.notification = Session::NoNotification;
|
|
}
|
|
|
|
updateTitle(qobject_cast<ViewProperties *>(controller));
|
|
updateColor(qobject_cast<ViewProperties *>(controller));
|
|
updateActivity(qobject_cast<ViewProperties *>(controller));
|
|
updateSpecialState(qobject_cast<ViewProperties *>(controller));
|
|
}
|
|
|
|
void TabbedViewContainer::closeTerminalTab(int idx)
|
|
{
|
|
Q_EMIT removeColor(idx);
|
|
// TODO: This for should probably go to the ViewSplitter
|
|
const auto viewSplitters = viewSplitterAt(idx)->findChildren<TerminalDisplay *>();
|
|
for (auto terminal : viewSplitters) {
|
|
terminal->sessionController()->closeSession();
|
|
}
|
|
}
|
|
|
|
ViewManager *TabbedViewContainer::connectedViewManager()
|
|
{
|
|
return _connectedViewManager;
|
|
}
|
|
|
|
void TabbedViewContainer::setNavigationVisibility(ViewManager::NavigationVisibility navigationVisibility)
|
|
{
|
|
if (navigationVisibility == ViewManager::NavigationNotSet) {
|
|
return;
|
|
}
|
|
|
|
setTabBarAutoHide(navigationVisibility == ViewManager::ShowNavigationAsNeeded);
|
|
if (navigationVisibility == ViewManager::AlwaysShowNavigation) {
|
|
tabBar()->setVisible(true);
|
|
} else if (navigationVisibility == ViewManager::AlwaysHideNavigation) {
|
|
tabBar()->setVisible(false);
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::toggleMaximizeCurrentTerminal()
|
|
{
|
|
if (auto *terminal = qobject_cast<TerminalDisplay *>(sender())) {
|
|
terminal->setFocus(Qt::FocusReason::OtherFocusReason);
|
|
}
|
|
|
|
activeViewSplitter()->toggleMaximizeCurrentTerminal();
|
|
}
|
|
|
|
|
|
void TabbedViewContainer::toggleZoomMaximizeCurrentTerminal()
|
|
{
|
|
if (auto *terminal = qobject_cast<TerminalDisplay *>(sender())) {
|
|
terminal->setFocus(Qt::FocusReason::OtherFocusReason);
|
|
}
|
|
|
|
activeViewSplitter()->toggleZoomMaximizeCurrentTerminal();
|
|
}
|
|
|
|
void TabbedViewContainer::moveTabLeft()
|
|
{
|
|
if (currentIndex() == 0) {
|
|
return;
|
|
}
|
|
tabBar()->moveTab(currentIndex(), currentIndex() - 1);
|
|
}
|
|
|
|
void TabbedViewContainer::moveTabRight()
|
|
{
|
|
if (currentIndex() == count() - 1) {
|
|
return;
|
|
}
|
|
tabBar()->moveTab(currentIndex(), currentIndex() + 1);
|
|
}
|
|
|
|
void TabbedViewContainer::setNavigationBehavior(int behavior)
|
|
{
|
|
_newTabBehavior = static_cast<NewTabBehavior>(behavior);
|
|
}
|
|
|
|
void TabbedViewContainer::moveToNewTab(TerminalDisplay *display)
|
|
{
|
|
// Ensure that the current terminal is not maximized so that the other views will be shown properly
|
|
activeViewSplitter()->clearMaximized();
|
|
addView(display);
|
|
}
|
|
|
|
#include "moc_ViewContainer.cpp"
|