diff --git a/TabBarSettings.ui b/TabBarSettings.ui
new file mode 100644
--- /dev/null
+++ b/TabBarSettings.ui
@@ -0,0 +1,217 @@
+
+
+ TabBarSettings
+
+
+
+ 0
+ 0
+ 503
+ 397
+
+
+
+ -
+
+
+ Appearance
+
+
+
-
+
+
+
+ 1
+ 0
+
+
+
-
+
+ Always Show Tab Bar
+
+
+ -
+
+ Show Tab Bar When Needed
+
+
+ -
+
+ Always Hide Tab Bar
+
+
+
+
+ -
+
+
+ Expand Individual Tab Widths to Full Window
+
+
+
+ -
+
+
+ Use user-defined stylesheet
+
+
+
+ -
+
+
+ Tab bar position:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 2
+ 0
+
+
+
+ text/css
+
+
+
+ -
+
+
+ Tab bar visibility:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Show 'New Tab' and 'Close Tab' buttons
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
-
+
+ Above Terminal Area
+
+
+ -
+
+ Below Terminal Area
+
+
+
+
+ -
+
+
+ QFrame::Box
+
+
+ QFrame::Plain
+
+
+ Starting with Qt 5.9, the next two options have different effects.
+
+
+ Qt::AlignCenter
+
+
+ true
+
+
+
+ -
+
+
+ Show the 'Close Tab' button individually
+
+
+
+
+
+
+ -
+
+
+ Behavior
+
+
+
-
+
+
+ New tab behavior:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
-
+
+ Put New Tab At The End
+
+
+ -
+
+ Put New Tab After Current Tab
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 4
+
+
+
+
+
+
+
+
+ KComboBox
+ QComboBox
+
+
+
+ KUrlRequester
+ QFrame
+
+ 1
+
+
+
+
+
diff --git a/ViewContainer.cpp b/ViewContainer.cpp
new file mode 100644
--- /dev/null
+++ b/ViewContainer.cpp
@@ -0,0 +1,535 @@
+/*
+ This file is part of the Konsole Terminal.
+
+ Copyright 2006-2008 Robert Knight
+
+ 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.
+*/
+
+// Own
+#include "ViewContainer.h"
+#include
+
+// Qt
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// KDE
+#include
+#include
+#include
+#include
+
+// Konsole
+#include "IncrementalSearchBar.h"
+#include "ViewProperties.h"
+#include "ProfileList.h"
+#include "ViewManager.h"
+#include "KonsoleSettings.h"
+#include "SessionController.h"
+#include "DetachableTabBar.h"
+#include "TerminalDisplay.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()),
+ _closeTabButton(new QToolButton()),
+ _contextMenuTabIndex(-1),
+ _navigationVisibility(ViewManager::NavigationVisibility::NavigationNotSet),
+ _tabHistoryIndex(-1)
+{
+ setAcceptDrops(true);
+
+ auto tabBarWidget = new DetachableTabBar();
+ setTabBar(tabBarWidget);
+ setDocumentMode(true);
+ setMovable(true);
+ connect(tabBarWidget, &DetachableTabBar::moveTabToWindow, this, &TabbedViewContainer::moveTabToWindow);
+ tabBar()->setContextMenuPolicy(Qt::CustomContextMenu);
+ _newTabButton->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
+ _newTabButton->setAutoRaise(true);
+ connect(_newTabButton, &QToolButton::clicked, this, [this]{
+ emit newViewRequest(this);
+ });
+
+ _closeTabButton->setIcon(QIcon::fromTheme(QStringLiteral("tab-close")));
+ _closeTabButton->setAutoRaise(true);
+ 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) {
+ emit detachTab(this, terminalAt(idx));
+ });
+ connect(this, &TabbedViewContainer::currentChanged, this, &TabbedViewContainer::currentTabChanged);
+
+ // 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);
+
+#if defined(ENABLE_DETACHING)
+ auto detachAction = _contextPopupMenu->addAction(
+ QIcon::fromTheme(QStringLiteral("tab-detach")),
+ i18nc("@action:inmenu", "&Detach Tab"), this,
+ [this] { emit detachTab(this, terminalAt(_contextMenuTabIndex)); }
+ );
+ detachAction->setObjectName(QStringLiteral("tab-detach"));
+#endif
+
+ auto editAction = _contextPopupMenu->addAction(
+ QIcon::fromTheme(QStringLiteral("edit-rename")),
+ i18nc("@action:inmenu", "&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();
+ auto profileList = new ProfileList(false, profileMenu);
+ profileList->syncWidgetActions(profileMenu, true);
+ connect(profileList, &Konsole::ProfileList::profileSelected, this,
+ [this](Profile::Ptr profile) { emit newViewWithProfileRequest(this, profile); });
+ _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);
+ }
+}
+
+TerminalDisplay *TabbedViewContainer::terminalAt(int index)
+{
+ return qobject_cast(widget(index));
+}
+
+void TabbedViewContainer::moveTabToWindow(int index, QWidget *window)
+{
+ const int id = terminalAt(index)->sessionController()->identifier();
+ // This one line here will be removed as soon as I finish my new split handling.
+ // it's hacky but it works.
+ const auto widgets = window->findChildren();
+ const auto currentPos = QCursor::pos();
+ for(const auto dropWidget : widgets) {
+ if (dropWidget->rect().contains(dropWidget->mapFromGlobal(currentPos))) {
+ emit dropWidget->moveViewRequest(-1, id);
+ removeView(terminalAt(index));
+ }
+ }
+}
+
+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::showQuickButtons() ? _newTabButton : nullptr, Qt::TopLeftCorner);
+ setCornerWidget( KonsoleSettings::showQuickButtons() ? _closeTabButton : nullptr, Qt::TopRightCorner);
+
+ tabBar()->setExpanding(KonsoleSettings::expandTabWidth());
+ tabBar()->update();
+ if (isVisible() && KonsoleSettings::showQuickButtons()) {
+ _newTabButton->setVisible(true);
+ _closeTabButton->setVisible(true);
+ }
+
+ tabBar()->setTabsClosable(KonsoleSettings::showCloseTabIndividually());
+
+ if (KonsoleSettings::tabBarUseUserStyleSheet()) {
+ setCssFromFile(KonsoleSettings::tabBarUserStyleSheetFile());
+ } else {
+ setCss();
+ }
+}
+
+void TabbedViewContainer::setCss(const QString& styleSheet)
+{
+ static const QString defaultCss = QStringLiteral("QTabWidget::tab-bar, QTabWidget::pane { margin: 0; }\n");
+ setStyleSheet(defaultCss + styleSheet);
+}
+
+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);
+ setCss(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 = terminalAt(newIndex);
+ auto currentWidget = terminalAt(currentIndex);
+ auto swappedContext = swappedWidget->sessionController();
+ auto currentContext = currentWidget->sessionController();
+
+ if (newIndex < currentIndex) {
+ insertTab(newIndex, currentWidget, currentContext->icon(), currentContext->title());
+ insertTab(currentIndex, swappedWidget, swappedContext->icon(), swappedContext->title());
+ } else {
+ insertTab(currentIndex, swappedWidget, swappedContext->icon(), swappedContext->title());
+ insertTab(newIndex, currentWidget, currentContext->icon(), currentContext->title());
+ }
+ setCurrentIndex(newIndex);
+}
+
+void TabbedViewContainer::addView(TerminalDisplay *view, int index)
+{
+ auto item = view->sessionController();
+ if (index == -1) {
+ addTab(view, item->icon(), item->title());
+ } else {
+ insertTab(index, view, item->icon(), item->title());
+ }
+
+ _tabHistory.append(view);
+ connect(item, &Konsole::ViewProperties::titleChanged, this,
+ &Konsole::TabbedViewContainer::updateTitle);
+ connect(item, &Konsole::ViewProperties::iconChanged, this,
+ &Konsole::TabbedViewContainer::updateIcon);
+ connect(item, &Konsole::ViewProperties::activity, this,
+ &Konsole::TabbedViewContainer::updateActivity);
+ connect(view, &QWidget::destroyed, this,
+ &Konsole::TabbedViewContainer::viewDestroyed);
+ emit viewAdded(view);
+}
+
+void TabbedViewContainer::viewDestroyed(QObject *view)
+{
+ auto widget = static_cast(view);
+ const auto idx = indexOf(widget);
+
+ removeTab(idx);
+ forgetView(widget);
+}
+
+void TabbedViewContainer::forgetView(TerminalDisplay *view)
+{
+ updateTabHistory(view, true);
+ emit viewRemoved(view);
+ if (count() == 0) {
+ emit empty(this);
+ }
+}
+
+void TabbedViewContainer::removeView(TerminalDisplay *view)
+{
+ const int idx = indexOf(view);
+ disconnect(view, &QWidget::destroyed, this, &Konsole::TabbedViewContainer::viewDestroyed);
+ removeTab(idx);
+ forgetView(view);
+}
+
+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::activateLastUsedView(bool reverse)
+{
+ if (_tabHistory.count() <= 1) {
+ return;
+ }
+
+ if (_tabHistoryIndex == -1) {
+ _tabHistoryIndex = reverse ? _tabHistory.count() - 1 : 1;
+ } else if (reverse) {
+ if (_tabHistoryIndex == 0) {
+ _tabHistoryIndex = _tabHistory.count() - 1;
+ } else {
+ _tabHistoryIndex--;
+ }
+ } else {
+ if (_tabHistoryIndex >= _tabHistory.count() - 1) {
+ _tabHistoryIndex = 0;
+ } else {
+ _tabHistoryIndex++;
+ }
+ }
+
+ int index = indexOf(_tabHistory[_tabHistoryIndex]);
+ setCurrentIndex(index);
+}
+
+void TabbedViewContainer::keyReleaseEvent(QKeyEvent* event)
+{
+ if (_tabHistoryIndex != -1 && event->modifiers() == Qt::NoModifier) {
+ _tabHistoryIndex = -1;
+ auto *active = qobject_cast(currentWidget());
+ if (active != _tabHistory[0]) {
+ // Update the tab history now that we have ended the walk-through
+ updateTabHistory(active);
+ }
+ }
+}
+
+void TabbedViewContainer::updateTabHistory(TerminalDisplay* view, bool remove)
+{
+ if (_tabHistoryIndex != -1 && !remove) {
+ // Do not reorder the tab history while we are walking through it
+ return;
+ }
+
+ for (int i = 0; i < _tabHistory.count(); ++i ) {
+ if (_tabHistory[i] == view) {
+ _tabHistory.removeAt(i);
+ if (!remove) {
+ _tabHistory.prepend(view);
+ }
+ break;
+ }
+ }
+}
+
+void TabbedViewContainer::closeCurrentTab()
+{
+ if (currentIndex() != -1) {
+ closeTerminalTab(currentIndex());
+ }
+}
+
+void TabbedViewContainer::tabDoubleClicked(int index)
+{
+ if (index >= 0) {
+ renameTab(index);
+ } else {
+ emit newViewRequest(this);
+ }
+}
+
+void TabbedViewContainer::renameTab(int index)
+{
+ if (index != -1) {
+ terminalAt(index)->sessionController()->rename();
+ }
+}
+
+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.
+#if defined(ENABLE_DETACHING)
+ for(auto action : _contextPopupMenu->actions()) {
+ if (action->objectName() == QStringLiteral("tab-detach")) {
+ action->setEnabled(count() > 1);
+ }
+ }
+#endif
+
+ // Add the read-only action
+ auto controller = terminalAt(_contextMenuTabIndex)->sessionController();
+ auto sessionController = qobject_cast(controller);
+
+ if (sessionController != nullptr) {
+ auto collection = sessionController->actionCollection();
+ auto readonlyAction = collection->action(QStringLiteral("view-readonly"));
+ if (readonlyAction != nullptr) {
+ const auto readonlyActions = _contextPopupMenu->actions();
+ _contextPopupMenu->insertAction(readonlyActions.last(), readonlyAction);
+ }
+
+ // Disable tab rename
+ for (auto &action : _contextPopupMenu->actions()) {
+ if (action->objectName() == QStringLiteral("edit-rename")) {
+ action->setEnabled(!sessionController->isReadOnly());
+ break;
+ }
+ }
+ }
+
+ _contextPopupMenu->exec(tabBar()->mapToGlobal(point));
+}
+
+void TabbedViewContainer::currentTabChanged(int index)
+{
+ if (index != -1) {
+ auto *view = terminalAt(index);
+ view->setFocus();
+ updateTabHistory(view);
+ emit activeViewChanged(view);
+ setTabActivity(index, false);
+ } 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::updateActivity(ViewProperties *item)
+{
+ auto controller = qobject_cast(item);
+ auto index = indexOf(controller->view());
+ if (index != currentIndex()) {
+ setTabActivity(index, true);
+ }
+}
+
+void TabbedViewContainer::updateTitle(ViewProperties *item)
+{
+ auto controller = qobject_cast(item);
+
+ const int index = indexOf(controller->view());
+ QString tabText = item->title();
+
+ setTabToolTip(index, tabText);
+
+ // To avoid having & replaced with _ (shortcut indicator)
+ tabText.replace(QLatin1Char('&'), QLatin1String("&&"));
+ setTabText(index, tabText);
+}
+
+void TabbedViewContainer::updateIcon(ViewProperties *item)
+{
+ auto controller = qobject_cast(item);
+ const int index = indexOf(controller->view());
+ setTabIcon(index, item->icon());
+}
+
+void TabbedViewContainer::closeTerminalTab(int idx) {
+ terminalAt(idx)->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);
+ }
+}
diff --git a/konsole.kcfg b/konsole.kcfg
new file mode 100644
--- /dev/null
+++ b/konsole.kcfg
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+ Show menubar by default in each Konsole window
+ true
+
+
+
+ Show window title set by escape sequence on the titlebar
+ false
+
+
+
+ Allow users to access top menu through Alt+Key combination
+ false
+
+
+
+ Show terminal size in columns and lines in the center of window after resizing
+ true
+
+
+
+ The window size will be saved upon exiting Konsole
+ true
+
+
+
+ When launching Konsole re-use existing process if possible
+ false
+
+
+
+
+
+ Sets whether the search is case sensitive
+ false
+
+
+
+ false
+
+
+
+ Sets whether matching text should be highlighted
+ true
+
+
+
+ Sets whether search should start from the bottom
+ true
+
+
+
+
+
+
+
+
+
+
+ ShowTabBarWhenNeeded
+
+
+
+
+
+
+
+ Bottom
+
+
+
+ QTabBar::tab { min-width: 2em ; max-width: 25em }
+
+
+
+ false
+
+
+
+
+
+
+
+ false
+
+
+
+ false
+
+
+
+
+
+
+
+ PutNewTabAtTheEnd
+
+
+
+ false
+
+
+
+
+
+ true
+
+
+
+ true
+
+
+
+
+
+ true
+
+
+
+ false
+
+
+
+ false
+
+
+
+
+
+
+