2023-02-23 12:47:39 +01:00
|
|
|
// Copyright (C) 2022 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
|
|
|
|
|
|
|
|
|
#include "terminalpane.h"
|
|
|
|
|
|
2023-02-25 10:47:21 +01:00
|
|
|
#include "shellmodel.h"
|
2023-03-23 11:04:54 +01:00
|
|
|
#include "terminalcommands.h"
|
2023-02-23 12:47:39 +01:00
|
|
|
#include "terminaltr.h"
|
|
|
|
|
#include "terminalwidget.h"
|
|
|
|
|
|
|
|
|
|
#include <coreplugin/actionmanager/actionmanager.h>
|
|
|
|
|
#include <coreplugin/icontext.h>
|
|
|
|
|
|
2023-03-06 11:54:21 +01:00
|
|
|
#include <projectexplorer/project.h>
|
|
|
|
|
#include <projectexplorer/projectmanager.h>
|
|
|
|
|
|
2023-02-23 15:21:26 +01:00
|
|
|
#include <utils/algorithm.h>
|
|
|
|
|
#include <utils/environment.h>
|
2023-03-23 11:04:54 +01:00
|
|
|
#include <utils/terminalhooks.h>
|
2023-02-23 12:47:39 +01:00
|
|
|
#include <utils/utilsicons.h>
|
|
|
|
|
|
2023-02-23 15:21:26 +01:00
|
|
|
#include <QMenu>
|
|
|
|
|
#include <QStandardPaths>
|
2023-03-23 12:28:44 +01:00
|
|
|
#include <QToolButton>
|
2023-02-23 15:21:26 +01:00
|
|
|
|
2023-02-23 12:47:39 +01:00
|
|
|
namespace Terminal {
|
|
|
|
|
|
2023-02-23 15:21:26 +01:00
|
|
|
using namespace Utils;
|
2023-03-07 17:55:38 +01:00
|
|
|
using namespace Utils::Terminal;
|
2023-02-23 15:21:26 +01:00
|
|
|
|
2023-02-23 12:47:39 +01:00
|
|
|
TerminalPane::TerminalPane(QObject *parent)
|
|
|
|
|
: Core::IOutputPane(parent)
|
2023-03-23 11:04:54 +01:00
|
|
|
, m_tabWidget(new QTabWidget)
|
2023-02-23 12:47:39 +01:00
|
|
|
{
|
2023-03-23 11:04:54 +01:00
|
|
|
setupContext("Terminal.Pane", m_tabWidget);
|
|
|
|
|
TerminalCommands::instance().init(Core::Context("Terminal.Pane"));
|
2023-02-23 12:47:39 +01:00
|
|
|
|
2023-03-23 11:04:54 +01:00
|
|
|
connect(this, &IOutputPane::zoomInRequested, this, [this] {
|
|
|
|
|
if (currentTerminal())
|
|
|
|
|
currentTerminal()->zoomIn();
|
|
|
|
|
});
|
|
|
|
|
connect(this, &IOutputPane::zoomOutRequested, this, [this] {
|
|
|
|
|
if (currentTerminal())
|
|
|
|
|
currentTerminal()->zoomOut();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
QAction &newTerminal = TerminalCommands::instance().paneActions().newTerminal;
|
|
|
|
|
QAction &closeTerminal = TerminalCommands::instance().paneActions().closeTerminal;
|
|
|
|
|
|
2023-03-23 13:46:58 +01:00
|
|
|
newTerminal.setIcon(
|
|
|
|
|
Icon({{":/terminal/images/settingscategory_terminal.png", Theme::Theme::IconsBaseColor}})
|
|
|
|
|
.icon());
|
2023-03-23 11:04:54 +01:00
|
|
|
newTerminal.setToolTip(Tr::tr("Create a new Terminal."));
|
2023-02-23 12:47:39 +01:00
|
|
|
|
2023-03-23 11:04:54 +01:00
|
|
|
connect(&newTerminal, &QAction::triggered, this, [this] { openTerminal({}); });
|
2023-02-23 12:47:39 +01:00
|
|
|
|
2023-03-23 11:04:54 +01:00
|
|
|
closeTerminal.setIcon(Icons::CLOSE_TOOLBAR.icon());
|
|
|
|
|
closeTerminal.setToolTip(Tr::tr("Close the current Terminal."));
|
|
|
|
|
closeTerminal.setEnabled(false);
|
2023-02-23 12:47:39 +01:00
|
|
|
|
2023-03-23 11:04:54 +01:00
|
|
|
connect(&closeTerminal, &QAction::triggered, this, [this] {
|
2023-02-23 12:47:39 +01:00
|
|
|
removeTab(m_tabWidget->currentIndex());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
m_newTerminalButton = new QToolButton();
|
2023-02-23 15:21:26 +01:00
|
|
|
|
|
|
|
|
QMenu *shellMenu = new QMenu(m_newTerminalButton);
|
2023-02-25 10:47:21 +01:00
|
|
|
Internal::ShellModel *shellModel = new Internal::ShellModel(shellMenu);
|
|
|
|
|
connect(shellMenu, &QMenu::aboutToShow, shellMenu, [shellMenu, shellModel, pane = this] {
|
|
|
|
|
shellMenu->clear();
|
2023-02-23 15:21:26 +01:00
|
|
|
|
2023-02-25 10:47:21 +01:00
|
|
|
const auto addItems = [shellMenu, pane](const QList<Internal::ShellModelItem> &items) {
|
|
|
|
|
for (const Internal::ShellModelItem &item : items) {
|
|
|
|
|
QAction *action = new QAction(item.icon, item.name, shellMenu);
|
2023-02-23 15:21:26 +01:00
|
|
|
|
2023-02-25 10:47:21 +01:00
|
|
|
connect(action, &QAction::triggered, action, [item, pane]() {
|
|
|
|
|
pane->openTerminal(item.openParameters);
|
|
|
|
|
});
|
2023-02-23 15:21:26 +01:00
|
|
|
|
2023-02-25 10:47:21 +01:00
|
|
|
shellMenu->addAction(action);
|
|
|
|
|
}
|
|
|
|
|
};
|
2023-02-23 15:21:26 +01:00
|
|
|
|
2023-02-25 10:47:21 +01:00
|
|
|
addItems(shellModel->local());
|
|
|
|
|
shellMenu->addSection(Tr::tr("Devices"));
|
|
|
|
|
addItems(shellModel->remote());
|
2023-02-23 15:21:26 +01:00
|
|
|
});
|
2023-02-25 10:47:21 +01:00
|
|
|
|
2023-03-23 11:04:54 +01:00
|
|
|
newTerminal.setMenu(shellMenu);
|
2023-02-23 15:21:26 +01:00
|
|
|
|
2023-03-23 11:04:54 +01:00
|
|
|
m_newTerminalButton->setDefaultAction(&newTerminal);
|
2023-02-23 12:47:39 +01:00
|
|
|
|
|
|
|
|
m_closeTerminalButton = new QToolButton();
|
2023-03-23 11:04:54 +01:00
|
|
|
m_closeTerminalButton->setDefaultAction(&closeTerminal);
|
|
|
|
|
|
|
|
|
|
connect(&TerminalCommands::instance().paneActions().nextTerminal,
|
|
|
|
|
&QAction::triggered,
|
|
|
|
|
this,
|
|
|
|
|
[this] {
|
|
|
|
|
if (canNavigate())
|
|
|
|
|
goToNext();
|
|
|
|
|
});
|
|
|
|
|
connect(&TerminalCommands::instance().paneActions().prevTerminal,
|
|
|
|
|
&QAction::triggered,
|
|
|
|
|
this,
|
|
|
|
|
[this] {
|
|
|
|
|
if (canPrevious())
|
|
|
|
|
goToPrev();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
connect(&TerminalCommands::instance().paneActions().minMax, &QAction::triggered, this, []() {
|
2023-03-09 20:08:49 +01:00
|
|
|
Core::Command *minMaxCommand = Core::ActionManager::command("Coreplugin.OutputPane.minmax");
|
|
|
|
|
if (minMaxCommand)
|
|
|
|
|
emit minMaxCommand->action()->triggered();
|
|
|
|
|
});
|
2023-03-23 12:28:44 +01:00
|
|
|
|
|
|
|
|
m_openSettingsButton = new QToolButton();
|
|
|
|
|
m_openSettingsButton->setToolTip(Tr::tr("Open Terminal Settings"));
|
|
|
|
|
m_openSettingsButton->setIcon(Icons::SETTINGS_TOOLBAR.icon());
|
|
|
|
|
|
|
|
|
|
connect(m_openSettingsButton, &QToolButton::clicked, m_openSettingsButton, []() {
|
|
|
|
|
TerminalCommands::openSettingsAction()->trigger();
|
|
|
|
|
});
|
2023-02-23 12:47:39 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-23 11:04:54 +01:00
|
|
|
TerminalPane::~TerminalPane()
|
|
|
|
|
{
|
|
|
|
|
delete m_tabWidget;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-06 11:54:21 +01:00
|
|
|
static std::optional<FilePath> startupProjectDirectory()
|
|
|
|
|
{
|
|
|
|
|
ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
|
|
|
|
|
if (!project)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
|
|
return project->projectDirectory();
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-07 17:55:38 +01:00
|
|
|
void TerminalPane::openTerminal(const OpenTerminalParameters ¶meters)
|
2023-02-23 12:47:39 +01:00
|
|
|
{
|
2023-03-07 17:55:38 +01:00
|
|
|
OpenTerminalParameters parametersCopy{parameters};
|
2023-02-23 12:47:39 +01:00
|
|
|
showPage(0);
|
2023-03-06 11:54:21 +01:00
|
|
|
|
2023-03-07 17:55:38 +01:00
|
|
|
if (!parametersCopy.workingDirectory) {
|
2023-03-06 11:54:21 +01:00
|
|
|
const std::optional<FilePath> projectDir = startupProjectDirectory();
|
|
|
|
|
if (projectDir) {
|
2023-03-07 17:55:38 +01:00
|
|
|
if (!parametersCopy.shellCommand
|
|
|
|
|
|| parametersCopy.shellCommand->executable().ensureReachable(*projectDir)) {
|
|
|
|
|
parametersCopy.workingDirectory = *projectDir;
|
2023-03-06 11:54:21 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-07 17:55:38 +01:00
|
|
|
auto terminalWidget = new TerminalWidget(m_tabWidget, parametersCopy);
|
2023-03-02 16:59:03 +01:00
|
|
|
m_tabWidget->setCurrentIndex(m_tabWidget->addTab(terminalWidget, Tr::tr("Terminal")));
|
|
|
|
|
setupTerminalWidget(terminalWidget);
|
2023-02-23 12:47:39 +01:00
|
|
|
|
2023-02-25 10:47:21 +01:00
|
|
|
m_tabWidget->currentWidget()->setFocus();
|
|
|
|
|
|
2023-03-23 11:04:54 +01:00
|
|
|
TerminalCommands::instance().paneActions().closeTerminal.setEnabled(m_tabWidget->count() > 1);
|
2023-02-23 12:47:39 +01:00
|
|
|
emit navigateStateUpdate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TerminalPane::addTerminal(TerminalWidget *terminal, const QString &title)
|
|
|
|
|
{
|
|
|
|
|
showPage(0);
|
|
|
|
|
m_tabWidget->setCurrentIndex(m_tabWidget->addTab(terminal, title));
|
2023-03-02 16:59:03 +01:00
|
|
|
setupTerminalWidget(terminal);
|
2023-02-23 12:47:39 +01:00
|
|
|
|
2023-03-23 11:04:54 +01:00
|
|
|
TerminalCommands::instance().paneActions().closeTerminal.setEnabled(m_tabWidget->count() > 1);
|
2023-02-23 12:47:39 +01:00
|
|
|
emit navigateStateUpdate();
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-07 17:55:38 +01:00
|
|
|
TerminalWidget *TerminalPane::stoppedTerminalWithId(const Id &identifier) const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(m_tabWidget, return nullptr);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < m_tabWidget->count(); ++i) {
|
|
|
|
|
auto terminal = qobject_cast<TerminalWidget *>(m_tabWidget->widget(i));
|
|
|
|
|
if (terminal->processState() == QProcess::NotRunning && terminal->identifier() == identifier)
|
|
|
|
|
return terminal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-23 12:47:39 +01:00
|
|
|
QWidget *TerminalPane::outputWidget(QWidget *parent)
|
|
|
|
|
{
|
2023-03-23 11:04:54 +01:00
|
|
|
if (!m_widgetInitialized) {
|
|
|
|
|
m_widgetInitialized = true;
|
2023-02-23 12:47:39 +01:00
|
|
|
m_tabWidget->setTabBarAutoHide(true);
|
|
|
|
|
m_tabWidget->setDocumentMode(true);
|
|
|
|
|
m_tabWidget->setTabsClosable(true);
|
|
|
|
|
m_tabWidget->setMovable(true);
|
|
|
|
|
|
|
|
|
|
connect(m_tabWidget, &QTabWidget::tabCloseRequested, this, [this](int index) {
|
|
|
|
|
removeTab(index);
|
|
|
|
|
});
|
|
|
|
|
|
2023-03-02 16:59:03 +01:00
|
|
|
auto terminalWidget = new TerminalWidget(parent);
|
|
|
|
|
m_tabWidget->addTab(terminalWidget, Tr::tr("Terminal"));
|
|
|
|
|
setupTerminalWidget(terminalWidget);
|
2023-02-23 12:47:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return m_tabWidget;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TerminalWidget *TerminalPane::currentTerminal() const
|
|
|
|
|
{
|
|
|
|
|
QWidget *activeWidget = m_tabWidget->currentWidget();
|
|
|
|
|
return static_cast<TerminalWidget *>(activeWidget);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TerminalPane::removeTab(int index)
|
|
|
|
|
{
|
|
|
|
|
if (m_tabWidget->count() > 1)
|
|
|
|
|
delete m_tabWidget->widget(index);
|
|
|
|
|
|
2023-03-23 11:04:54 +01:00
|
|
|
TerminalCommands::instance().paneActions().closeTerminal.setEnabled(m_tabWidget->count() > 1);
|
2023-03-23 13:34:59 +01:00
|
|
|
|
|
|
|
|
if (auto terminal = currentTerminal()) {
|
|
|
|
|
terminal->setFocus();
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-23 12:47:39 +01:00
|
|
|
emit navigateStateUpdate();
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-02 16:59:03 +01:00
|
|
|
void TerminalPane::setupTerminalWidget(TerminalWidget *terminal)
|
|
|
|
|
{
|
|
|
|
|
if (!terminal)
|
|
|
|
|
return;
|
|
|
|
|
|
2023-03-14 09:09:55 +01:00
|
|
|
auto setTabText = [this](TerminalWidget *terminal) {
|
2023-03-02 16:59:03 +01:00
|
|
|
auto index = m_tabWidget->indexOf(terminal);
|
2023-03-10 13:55:17 +01:00
|
|
|
const FilePath cwd = terminal->cwd();
|
|
|
|
|
|
2023-03-14 09:09:55 +01:00
|
|
|
const QString exe = terminal->currentCommand().isEmpty()
|
|
|
|
|
? terminal->shellName()
|
|
|
|
|
: terminal->currentCommand().executable().fileName();
|
2023-03-10 13:55:17 +01:00
|
|
|
|
|
|
|
|
if (cwd.isEmpty())
|
|
|
|
|
m_tabWidget->setTabText(index, exe);
|
|
|
|
|
else
|
|
|
|
|
m_tabWidget->setTabText(index, exe + " - " + cwd.fileName());
|
2023-03-02 16:59:03 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
connect(terminal, &TerminalWidget::started, [setTabText, terminal](qint64 /*pid*/) {
|
|
|
|
|
setTabText(terminal);
|
|
|
|
|
});
|
|
|
|
|
|
2023-03-10 13:55:17 +01:00
|
|
|
connect(terminal, &TerminalWidget::cwdChanged, [setTabText, terminal]() {
|
|
|
|
|
setTabText(terminal);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
connect(terminal, &TerminalWidget::commandChanged, [setTabText, terminal]() {
|
|
|
|
|
setTabText(terminal);
|
|
|
|
|
});
|
|
|
|
|
|
2023-03-02 16:59:03 +01:00
|
|
|
if (!terminal->shellName().isEmpty())
|
|
|
|
|
setTabText(terminal);
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-23 12:47:39 +01:00
|
|
|
QList<QWidget *> TerminalPane::toolBarWidgets() const
|
|
|
|
|
{
|
2023-03-23 11:04:54 +01:00
|
|
|
QList<QWidget *> widgets = IOutputPane::toolBarWidgets();
|
|
|
|
|
widgets.prepend(m_newTerminalButton);
|
|
|
|
|
widgets.prepend(m_closeTerminalButton);
|
|
|
|
|
|
2023-03-23 12:28:44 +01:00
|
|
|
return widgets << m_openSettingsButton;
|
2023-02-23 12:47:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString TerminalPane::displayName() const
|
|
|
|
|
{
|
|
|
|
|
return Tr::tr("Terminal");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int TerminalPane::priorityInStatusBar() const
|
|
|
|
|
{
|
|
|
|
|
return 50;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TerminalPane::clearContents()
|
|
|
|
|
{
|
|
|
|
|
if (const auto t = currentTerminal())
|
|
|
|
|
t->clearContents();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TerminalPane::visibilityChanged(bool visible)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(visible);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TerminalPane::setFocus()
|
|
|
|
|
{
|
|
|
|
|
if (const auto t = currentTerminal())
|
|
|
|
|
t->setFocus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TerminalPane::hasFocus() const
|
|
|
|
|
{
|
|
|
|
|
if (const auto t = currentTerminal())
|
2023-02-27 11:06:14 +01:00
|
|
|
return t->hasFocus();
|
2023-02-23 12:47:39 +01:00
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TerminalPane::canFocus() const
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TerminalPane::canNavigate() const
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TerminalPane::canNext() const
|
|
|
|
|
{
|
2023-03-02 18:13:27 +01:00
|
|
|
return m_tabWidget->count() > 1;
|
2023-02-23 12:47:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TerminalPane::canPrevious() const
|
|
|
|
|
{
|
2023-03-02 18:13:27 +01:00
|
|
|
return m_tabWidget->count() > 1;
|
2023-02-23 12:47:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TerminalPane::goToNext()
|
|
|
|
|
{
|
2023-03-02 18:13:27 +01:00
|
|
|
int nextIndex = m_tabWidget->currentIndex() + 1;
|
|
|
|
|
if (nextIndex >= m_tabWidget->count())
|
|
|
|
|
nextIndex = 0;
|
|
|
|
|
|
|
|
|
|
m_tabWidget->setCurrentIndex(nextIndex);
|
2023-02-23 12:47:39 +01:00
|
|
|
emit navigateStateUpdate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TerminalPane::goToPrev()
|
|
|
|
|
{
|
2023-03-02 18:13:27 +01:00
|
|
|
int prevIndex = m_tabWidget->currentIndex() - 1;
|
|
|
|
|
if (prevIndex < 0)
|
|
|
|
|
prevIndex = m_tabWidget->count() - 1;
|
|
|
|
|
|
|
|
|
|
m_tabWidget->setCurrentIndex(prevIndex);
|
2023-02-23 12:47:39 +01:00
|
|
|
emit navigateStateUpdate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Terminal
|