2023-02-25 10:47:21 +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 "shellmodel.h"
|
|
|
|
|
|
2023-08-10 06:44:27 +02:00
|
|
|
#include <projectexplorer/devicesupport/devicemanager.h>
|
|
|
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
|
|
|
|
|
2023-02-25 10:47:21 +01:00
|
|
|
#include <utils/algorithm.h>
|
|
|
|
|
#include <utils/environment.h>
|
|
|
|
|
#include <utils/filepath.h>
|
|
|
|
|
|
|
|
|
|
#include <QFileIconProvider>
|
|
|
|
|
#include <QStandardPaths>
|
|
|
|
|
|
|
|
|
|
namespace Terminal::Internal {
|
|
|
|
|
|
|
|
|
|
using namespace Utils;
|
|
|
|
|
|
2023-06-25 05:59:43 +03:00
|
|
|
struct ShellItemBuilder
|
2023-02-25 10:47:21 +01:00
|
|
|
{
|
2023-07-03 14:33:21 +03:00
|
|
|
explicit ShellItemBuilder(const QFileIconProvider &iconProvider, const CommandLine &value) :
|
|
|
|
|
iconProvider(iconProvider)
|
2023-06-25 05:59:43 +03:00
|
|
|
{
|
|
|
|
|
m_item.openParameters.shellCommand = value;
|
|
|
|
|
}
|
2023-07-03 14:33:21 +03:00
|
|
|
explicit ShellItemBuilder(const QFileIconProvider &iconProvider, const FilePath &value) :
|
|
|
|
|
iconProvider(iconProvider)
|
2023-06-25 05:59:43 +03:00
|
|
|
{
|
|
|
|
|
m_item.openParameters.shellCommand = CommandLine(value);
|
|
|
|
|
}
|
|
|
|
|
ShellItemBuilder &name(const QString &value)
|
|
|
|
|
{
|
|
|
|
|
m_item.name = value;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
ShellItemBuilder &icon(const FilePath &value)
|
|
|
|
|
{
|
2023-07-03 14:33:21 +03:00
|
|
|
m_item.openParameters.icon = iconProvider.icon(value.toFileInfo());
|
2023-06-25 05:59:43 +03:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline operator ShellModelItem() { return item(); }
|
|
|
|
|
ShellModelItem item()
|
|
|
|
|
{
|
|
|
|
|
if (m_item.name.isEmpty())
|
|
|
|
|
m_item.name = m_item.openParameters.shellCommand->executable().toUserOutput();
|
2023-07-03 14:33:21 +03:00
|
|
|
if (m_item.openParameters.icon.isNull())
|
2023-06-25 05:59:43 +03:00
|
|
|
icon(m_item.openParameters.shellCommand->executable());
|
|
|
|
|
return m_item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
ShellModelItem m_item;
|
2023-07-03 14:33:21 +03:00
|
|
|
const QFileIconProvider &iconProvider;
|
2023-06-25 05:59:43 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static QSet<FilePath> msysPaths()
|
|
|
|
|
{
|
|
|
|
|
QSet<FilePath> res;
|
|
|
|
|
FilePath msys2 = FilePath::fromUserInput(QStandardPaths::findExecutable("msys2.exe"));
|
|
|
|
|
if (msys2.exists())
|
|
|
|
|
res.insert(msys2.parentDir());
|
|
|
|
|
for (const QFileInfo &drive : QDir::drives()) {
|
|
|
|
|
for (const QString &name : QStringList{"msys2", "msys32", "msys64"}) {
|
|
|
|
|
msys2 = FilePath::fromString(drive.filePath()).pathAppended(name);
|
|
|
|
|
if (msys2.pathAppended("msys2.exe").exists())
|
|
|
|
|
res.insert(msys2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ShellModelPrivate
|
|
|
|
|
{
|
|
|
|
|
QList<ShellModelItem> localShells;
|
|
|
|
|
ShellModelPrivate();
|
|
|
|
|
};
|
2023-02-25 10:47:21 +01:00
|
|
|
|
2023-06-25 05:59:43 +03:00
|
|
|
ShellModelPrivate::ShellModelPrivate()
|
|
|
|
|
{
|
2023-07-03 14:33:21 +03:00
|
|
|
QFileIconProvider iconProvider;
|
2023-06-25 05:59:43 +03:00
|
|
|
if (Utils::HostOsInfo::isWindowsHost()) {
|
|
|
|
|
const FilePath comspec = FilePath::fromUserInput(qtcEnvironmentVariable("COMSPEC"));
|
2023-07-03 14:33:21 +03:00
|
|
|
localShells << ShellItemBuilder(iconProvider, comspec);
|
2023-02-25 10:47:21 +01:00
|
|
|
|
|
|
|
|
if (comspec.fileName() != "cmd.exe") {
|
|
|
|
|
FilePath cmd = FilePath::fromUserInput(QStandardPaths::findExecutable("cmd.exe"));
|
2023-07-03 14:33:21 +03:00
|
|
|
localShells << ShellItemBuilder(iconProvider, cmd);
|
2023-02-25 10:47:21 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-25 05:59:43 +03:00
|
|
|
const FilePath powershell = FilePath::fromUserInput(
|
2023-02-25 10:47:21 +01:00
|
|
|
QStandardPaths::findExecutable("powershell.exe"));
|
|
|
|
|
|
2023-06-25 05:59:43 +03:00
|
|
|
if (powershell.exists())
|
2023-07-03 14:33:21 +03:00
|
|
|
localShells << ShellItemBuilder(iconProvider, powershell);
|
2023-06-25 05:59:43 +03:00
|
|
|
|
|
|
|
|
const FilePath sys_bash =
|
|
|
|
|
FilePath::fromUserInput(QStandardPaths::findExecutable("bash.exe"));
|
|
|
|
|
if (sys_bash.exists())
|
2023-07-03 14:33:21 +03:00
|
|
|
localShells << ShellItemBuilder(iconProvider, {sys_bash, {"--login"}});
|
2023-06-25 05:59:43 +03:00
|
|
|
|
|
|
|
|
const FilePath git_exe = FilePath::fromUserInput(QStandardPaths::findExecutable("git.exe"));
|
|
|
|
|
if (git_exe.exists()) {
|
|
|
|
|
FilePath git_bash = git_exe.parentDir().parentDir().pathAppended("bin/bash.exe");
|
|
|
|
|
if (git_bash.exists()) {
|
2023-07-03 14:33:21 +03:00
|
|
|
localShells << ShellItemBuilder(iconProvider, {git_bash, {"--login"}})
|
2023-06-25 05:59:43 +03:00
|
|
|
.icon(git_bash.parentDir().parentDir().pathAppended("git-bash.exe"));
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-25 10:47:21 +01:00
|
|
|
|
2023-06-25 05:59:43 +03:00
|
|
|
const QStringList msys2_args{"-defterm", "-no-start", "-here"};
|
|
|
|
|
for (const FilePath &msys2_path : msysPaths()) {
|
|
|
|
|
const FilePath msys2_cmd = msys2_path.pathAppended("msys2_shell.cmd");
|
|
|
|
|
for (const FilePath &type : msys2_path.dirEntries({{"*.ico"}})) {
|
|
|
|
|
// Only list this type if it has anything in /bin
|
|
|
|
|
QDirIterator it(type.path().replace(".ico", "/bin"), QDir::Files);
|
|
|
|
|
if (!it.hasNext())
|
|
|
|
|
continue;
|
2023-07-03 14:33:21 +03:00
|
|
|
localShells << ShellItemBuilder(iconProvider,
|
2023-06-25 05:59:43 +03:00
|
|
|
{msys2_cmd, msys2_args + QStringList{"-" + type.baseName()}})
|
|
|
|
|
.icon(type)
|
|
|
|
|
.name(type.toUserOutput().replace(".ico", ".exe"));
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-25 10:47:21 +01:00
|
|
|
} else {
|
|
|
|
|
FilePath shellsFile = FilePath::fromString("/etc/shells");
|
|
|
|
|
const auto shellFileContent = shellsFile.fileContents();
|
2023-06-25 05:59:43 +03:00
|
|
|
QTC_ASSERT_EXPECTED(shellFileContent, return);
|
2023-02-25 10:47:21 +01:00
|
|
|
|
|
|
|
|
QString shellFileContentString = QString::fromUtf8(*shellFileContent);
|
|
|
|
|
|
|
|
|
|
// Filter out comments ...
|
|
|
|
|
const QStringList lines
|
|
|
|
|
= Utils::filtered(shellFileContentString.split('\n', Qt::SkipEmptyParts),
|
|
|
|
|
[](const QString &line) { return !line.trimmed().startsWith('#'); });
|
|
|
|
|
|
|
|
|
|
// Convert lines to file paths ...
|
|
|
|
|
const FilePaths shells = Utils::transform(lines, [](const QString &line) {
|
|
|
|
|
return FilePath::fromUserInput(line.trimmed());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ... and filter out non-existing shells.
|
2023-08-10 06:44:27 +02:00
|
|
|
localShells = Utils::transform(Utils::filtered(shells, &FilePath::exists),
|
|
|
|
|
[&iconProvider](const FilePath &shell) {
|
|
|
|
|
return ShellItemBuilder(iconProvider, shell).item();
|
|
|
|
|
});
|
2023-02-25 10:47:21 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ShellModel::ShellModel(QObject *parent)
|
|
|
|
|
: QObject(parent)
|
|
|
|
|
, d(new ShellModelPrivate())
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ShellModel::~ShellModel() = default;
|
|
|
|
|
|
|
|
|
|
QList<ShellModelItem> ShellModel::local() const
|
|
|
|
|
{
|
|
|
|
|
return d->localShells;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<ShellModelItem> ShellModel::remote() const
|
|
|
|
|
{
|
2023-08-10 06:44:27 +02:00
|
|
|
QList<ShellModelItem> result;
|
2023-02-25 10:47:21 +01:00
|
|
|
|
2023-08-10 06:44:27 +02:00
|
|
|
ProjectExplorer::DeviceManager::instance()->forEachDevice(
|
2024-02-01 20:09:12 +01:00
|
|
|
[&result](const std::shared_ptr<const ProjectExplorer::IDevice> &device) {
|
2023-08-10 06:44:27 +02:00
|
|
|
if (device->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE)
|
2024-05-16 12:31:24 +02:00
|
|
|
result << ShellModelItem{device->displayName(), {CommandLine{device->rootPath()}}};
|
2023-03-01 09:57:43 +01:00
|
|
|
});
|
2023-02-25 10:47:21 +01:00
|
|
|
|
2023-08-10 06:44:27 +02:00
|
|
|
return result;
|
2023-02-25 10:47:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Terminal::Internal
|