You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
qterminal-packaging/src/tabwidget.cpp

512 lines
15 KiB

/***************************************************************************
* Copyright (C) 2006 by Vladimir Kuznetsov *
* vovanec@gmail.com *
* *
* 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, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include <QTabBar>
#include <QInputDialog>
#include <QColorDialog>
#include <QMouseEvent>
#include <QMenu>
#include "mainwindow.h"
#include "termwidgetholder.h"
#include "tabbar.h"
#include "tabwidget.h"
#include "config.h"
#include "properties.h"
#include "qterminalapp.h"
#define TAB_INDEX_PROPERTY "tab_index"
#define TAB_CUSTOM_NAME_PROPERTY "custom_name"
TabWidget::TabWidget(QWidget* parent) : QTabWidget(parent), tabNumerator(0), mTabBar(new TabBar(this))
{
// Insert our own tab bar which overrides tab width and eliding
setTabBar(mTabBar);
setFocusPolicy(Qt::NoFocus);
/* On Mac OS X this will look similar to
* the tabs in Safari or Leopard's Terminal.app .
* I love this!
*/
setDocumentMode(true);
tabBar()->setUsesScrollButtons(true);
setMovable(true);
setUsesScrollButtons(true);
tabBar()->installEventFilter(this);
connect(this, &TabWidget::tabCloseRequested, this, &TabWidget::removeTab);
connect(tabBar(), &QTabBar::tabMoved, this, &TabWidget::updateTabIndices);
connect(this, &TabWidget::tabRenameRequested, this, &TabWidget::renameSession);
connect(this, &TabWidget::tabTitleColorChangeRequested, this, &TabWidget::setTitleColor);
}
TermWidgetHolder * TabWidget::terminalHolder()
{
return reinterpret_cast<TermWidgetHolder*>(widget(currentIndex()));
}
int TabWidget::addNewTab(TerminalConfig config)
{
tabNumerator++;
QString label = QString(tr("Shell No. %1")).arg(tabNumerator);
TermWidgetHolder *ch = terminalHolder();
if (ch)
config.provideCurrentDirectory(ch->currentTerminal()->impl()->workingDirectory());
TermWidgetHolder *console = new TermWidgetHolder(config, this);
console->setWindowTitle(label);
connect(console, &TermWidgetHolder::finished, this, &TabWidget::removeFinished);
connect(console, &TermWidgetHolder::lastTerminalClosed, this, &TabWidget::removeFinished);
connect(console, &TermWidgetHolder::termTitleChanged, this, &TabWidget::onTermTitleChanged);
connect(this, &QTabWidget::currentChanged, this, &TabWidget::currentTitleChanged);
int index = addTab(console, label);
console->setProperty(TAB_CUSTOM_NAME_PROPERTY, false);
updateTabIndices();
setCurrentIndex(index);
console->setInitialFocus();
showHideTabBar();
return index;
}
void TabWidget::switchLeftSubterminal()
{
terminalHolder()->directionalNavigation(NavigationDirection::Left);
}
void TabWidget::switchRightSubterminal()
{
terminalHolder()->directionalNavigation(NavigationDirection::Right);
}
void TabWidget::switchTopSubterminal() {
terminalHolder()->directionalNavigation(NavigationDirection::Top);
}
void TabWidget::switchBottomSubterminal() {
terminalHolder()->directionalNavigation(NavigationDirection::Bottom);
}
void TabWidget::splitHorizontally()
{
terminalHolder()->splitHorizontal(terminalHolder()->currentTerminal());
findParent<MainWindow>(this)->updateDisabledActions();
}
void TabWidget::splitVertically()
{
terminalHolder()->splitVertical(terminalHolder()->currentTerminal());
findParent<MainWindow>(this)->updateDisabledActions();
}
void TabWidget::splitCollapse()
{
terminalHolder()->splitCollapse(terminalHolder()->currentTerminal());
findParent<MainWindow>(this)->updateDisabledActions();
}
void TabWidget::copySelection()
{
terminalHolder()->currentTerminal()->impl()->copyClipboard();
}
void TabWidget::pasteClipboard()
{
terminalHolder()->currentTerminal()->impl()->pasteClipboard();
}
void TabWidget::pasteSelection()
{
terminalHolder()->currentTerminal()->impl()->pasteSelection();
}
void TabWidget::zoomIn()
{
terminalHolder()->currentTerminal()->impl()->zoomIn();
}
void TabWidget::zoomOut()
{
terminalHolder()->currentTerminal()->impl()->zoomOut();
}
void TabWidget::zoomReset()
{
terminalHolder()->currentTerminal()->impl()->zoomReset();
}
void TabWidget::updateTabIndices()
{
for(int i = 0; i < count(); i++)
widget(i)->setProperty(TAB_INDEX_PROPERTY, i);
}
void TabWidget::onTermTitleChanged(QString title, QString icon)
{
TermWidgetHolder * console = qobject_cast<TermWidgetHolder*>(sender());
const bool custom_name = console->property(TAB_CUSTOM_NAME_PROPERTY).toBool();
if (!custom_name)
{
const int index = console->property(TAB_INDEX_PROPERTY).toInt();
setTabIcon(index, QIcon::fromTheme(icon));
setTabText(index, title);
if (currentIndex() == index)
emit currentTitleChanged(index);
}
}
void TabWidget::renameSession(int index)
{
bool ok = false;
QString text = QInputDialog::getText(this, tr("Tab name"),
tr("New tab name:"), QLineEdit::Normal,
QString(), &ok);
if(ok && !text.isEmpty())
{
setTabIcon(index, QIcon{});
setTabText(index, text);
widget(index)->setProperty(TAB_CUSTOM_NAME_PROPERTY, true);
if (currentIndex() == index)
emit currentTitleChanged(index);
}
}
void TabWidget::renameCurrentSession()
{
renameSession(currentIndex());
}
void TabWidget::setTitleColor(int index)
{
QColor current = tabBar()->tabTextColor(index);
QColor color = QColorDialog::getColor(current, this, tr("Select new tab title color"));
if (color.isValid())
tabBar()->setTabTextColor(index, color);
}
void TabWidget::renameTabsAfterRemove()
{
// it breaks custom names - it replaces original/custom title with shell no #
#if 0
for(int i = 0; i < count(); i++) {
setTabText(i, QString(tr("Shell No. %1")).arg(i+1));
}
#endif
}
void TabWidget::contextMenuEvent(QContextMenuEvent *event)
{
QMenu menu(this);
QMap< QString, QAction * > actions = findParent<MainWindow>(this)->leaseActions();
QAction *close = menu.addAction(QIcon::fromTheme("document-close"), tr("Close session"));
QAction *rename = menu.addAction(actions[RENAME_SESSION]->text());
QAction *changeColor = menu.addAction(QIcon::fromTheme("color-management"), tr("Change title color"));
rename->setShortcut(actions[RENAME_SESSION]->shortcut());
rename->blockSignals(true);
int tabIndex = tabBar()->tabAt(tabBar()->mapFrom(this,event->pos()));
QAction *action = menu.exec(event->globalPos());
if (action == close) {
emit tabCloseRequested(tabIndex);
} else if (action == rename) {
emit tabRenameRequested(tabIndex);
} else if (action == changeColor) {
emit tabTitleColorChangeRequested(tabIndex);
}
}
bool TabWidget::eventFilter(QObject *obj, QEvent *event)
{
QMouseEvent *e = reinterpret_cast<QMouseEvent*>(event);
if (e->button() == Qt::MidButton) {
if (event->type() == QEvent::MouseButtonRelease) {
// close the tab on middle clicking
int index = tabBar()->tabAt(e->pos());
if (index > -1){
removeTab(index);
return true;
}
}
}
else if (event->type() == QEvent::MouseButtonDblClick)
{
// if user doubleclicks on tab button - rename it. If user
// clicks on free space - open new tab
int index = tabBar()->tabAt(e->pos());
if (index == -1)
{
TerminalConfig defaultConfig;
addNewTab(defaultConfig);
}
else
renameSession(index);
return true;
}
return QTabWidget::eventFilter(obj, event);
}
void TabWidget::removeFinished()
{
QObject* term = sender();
QVariant prop = term->property(TAB_INDEX_PROPERTY);
if(prop.isValid() && prop.canConvert(QVariant::Int))
{
int index = prop.toInt();
removeTab(index);
// if (count() == 0)
// emit closeTabNotification();
}
}
void TabWidget::removeTab(int index)
{
if (count() > 1) {
setUpdatesEnabled(false);
QWidget * w = widget(index);
QTabWidget::removeTab(index);
w->deleteLater();
updateTabIndices();
int current = currentIndex();
if (current >= 0 )
{
qobject_cast<TermWidgetHolder*>(widget(current))->setInitialFocus();
}
// do not decrease it as renaming is disabled in renameTabsAfterRemove
// tabNumerator--;
setUpdatesEnabled(true);
} else {
emit closeTabNotification(true);
}
renameTabsAfterRemove();
showHideTabBar();
}
void TabWidget::removeCurrentTab()
{
// question disabled due user requests. Yes I agree it was anoying.
// if (QMessageBox::question(this,
// tr("Close current session"),
// tr("Are you sure you want to close current sesstion?"),
// QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
// {
if (count() > 1) {
removeTab(currentIndex());
} else {
emit closeTabNotification(false);
}
}
int TabWidget::switchToRight()
{
int next_pos = currentIndex() + 1;
if (next_pos < count())
setCurrentIndex(next_pos);
else
setCurrentIndex(0);
findParent<MainWindow>(this)->updateDisabledActions();
return currentIndex();
}
int TabWidget::switchToLeft()
{
int previous_pos = currentIndex() - 1;
if (previous_pos < 0)
setCurrentIndex(count() - 1);
else
setCurrentIndex(previous_pos);
findParent<MainWindow>(this)->updateDisabledActions();
return currentIndex();
}
void TabWidget::move(Direction dir)
{
if(count() > 1)
{
int index = currentIndex();
QWidget* child = widget(index);
QString label = tabText(index);
QString toolTip = tabToolTip(index);
QIcon icon = tabIcon(index);
int newIndex = 0;
if(dir == Left)
if(index == 0)
newIndex = count() -1;
else
newIndex = index - 1;
else
if(index == count() - 1)
newIndex = 0;
else
newIndex = index + 1;
setUpdatesEnabled(false);
QTabWidget::removeTab(index);
newIndex = insertTab(newIndex, child, label);
setTabToolTip(newIndex, toolTip);
setTabIcon(newIndex, icon);
setUpdatesEnabled(true);
setCurrentIndex(newIndex);
child->setFocus();
updateTabIndices();
}
}
void TabWidget::moveLeft()
{
move(Left);
}
void TabWidget::moveRight()
{
move(Right);
}
void TabWidget::changeScrollPosition(QAction *triggered)
{
QActionGroup *scrollPosition = static_cast<QActionGroup *>(sender());
if(!scrollPosition)
qFatal("scrollPosition is NULL");
Properties::Instance()->scrollBarPos =
scrollPosition->actions().indexOf(triggered);
Properties::Instance()->saveSettings();
propertiesChanged();
}
void TabWidget::changeTabPosition(QAction *triggered)
{
QActionGroup *tabPosition = static_cast<QActionGroup *>(sender());
if(!tabPosition)
qFatal("tabPosition is NULL");
Properties *prop = Properties::Instance();
/* order is dictated from mainwindow.cpp */
QTabWidget::TabPosition position = (QTabWidget::TabPosition)tabPosition->actions().indexOf(triggered);
setTabPosition(position);
prop->tabsPos = position;
prop->saveSettings();
}
void TabWidget::changeKeyboardCursorShape(QAction *triggered)
{
QActionGroup *keyboardCursorShape = static_cast<QActionGroup *>(sender());
if(!keyboardCursorShape)
qFatal("keyboardCursorShape is NULL");
Properties::Instance()->keyboardCursorShape =
keyboardCursorShape->actions().indexOf(triggered);
Properties::Instance()->saveSettings();
propertiesChanged();
}
void TabWidget::propertiesChanged()
{
for (int i = 0; i < count(); ++i)
{
TermWidgetHolder *console = static_cast<TermWidgetHolder*>(widget(i));
console->propertiesChanged();
}
showHideTabBar();
setTabsClosable(Properties::Instance()->showCloseTabButton);
// Update the tab widths
mTabBar->setLimitWidth(Properties::Instance()->limitTabWidth);
mTabBar->setLimitWidthValue(Properties::Instance()->limitTabWidthValue);
mTabBar->updateWidth();
}
void TabWidget::clearActiveTerminal()
{
reinterpret_cast<TermWidgetHolder*>(widget(currentIndex()))->clearActiveTerminal();
}
void TabWidget::saveSession()
{
int ix = currentIndex();
reinterpret_cast<TermWidgetHolder*>(widget(ix))->saveSession(tabText(ix));
}
void TabWidget::loadSession()
{
reinterpret_cast<TermWidgetHolder*>(widget(currentIndex()))->loadSession();
}
void TabWidget::preset2Horizontal()
{
TerminalConfig defaultConfig;
int ix = TabWidget::addNewTab(defaultConfig);
TermWidgetHolder* term = reinterpret_cast<TermWidgetHolder*>(widget(ix));
term->splitHorizontal(term->currentTerminal());
// switch to the 1st terminal
term->directionalNavigation(NavigationDirection::Left);
}
void TabWidget::preset2Vertical()
{
TerminalConfig defaultConfig;
int ix = TabWidget::addNewTab(defaultConfig);
TermWidgetHolder* term = reinterpret_cast<TermWidgetHolder*>(widget(ix));
term->splitVertical(term->currentTerminal());
// switch to the 1st terminal
term->directionalNavigation(NavigationDirection::Left);
}
void TabWidget::preset4Terminals()
{
TerminalConfig defaultConfig;
int ix = TabWidget::addNewTab(defaultConfig);
TermWidgetHolder* term = reinterpret_cast<TermWidgetHolder*>(widget(ix));
term->splitVertical(term->currentTerminal());
term->splitHorizontal(term->currentTerminal());
term->directionalNavigation(NavigationDirection::Left);
term->splitHorizontal(term->currentTerminal());
// switch to the 1st terminal
term->directionalNavigation(NavigationDirection::Top);
}
void TabWidget::showHideTabBar()
{
if (Properties::Instance()->tabBarless)
tabBar()->setVisible(false);
else
tabBar()->setVisible(!Properties::Instance()->hideTabBarWithOneTab || count() > 1);
}