feat: add Konsole recipe source and patches
This commit is contained in:
@@ -0,0 +1,555 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "Application.h"
|
||||
|
||||
// Qt
|
||||
#include <QApplication>
|
||||
#include <QCommandLineParser>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QHash>
|
||||
#include <QStandardPaths>
|
||||
#include <QTimer>
|
||||
|
||||
// KDE
|
||||
#include <KActionCollection>
|
||||
#if HAVE_DBUS
|
||||
#include <KGlobalAccel>
|
||||
#endif
|
||||
#include <KLocalizedString>
|
||||
|
||||
// Konsole
|
||||
#include "KonsoleSettings.h"
|
||||
#include "MainWindow.h"
|
||||
#include "ShellCommand.h"
|
||||
#include "ViewManager.h"
|
||||
#include "WindowSystemInfo.h"
|
||||
#include "profile/ProfileCommandParser.h"
|
||||
#include "profile/ProfileManager.h"
|
||||
#include "session/Session.h"
|
||||
#include "session/SessionManager.h"
|
||||
#include "widgets/ViewContainer.h"
|
||||
|
||||
#include "pluginsystem/IKonsolePlugin.h"
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
Application::Application(QSharedPointer<QCommandLineParser> parser, const QStringList &customCommand)
|
||||
: _backgroundInstance(nullptr)
|
||||
, m_parser(parser)
|
||||
, m_customCommand(customCommand)
|
||||
{
|
||||
m_pluginManager.loadAllPlugins();
|
||||
}
|
||||
|
||||
void Application::populateCommandLineParser(QCommandLineParser *parser)
|
||||
{
|
||||
const auto options = QVector<QCommandLineOption>{
|
||||
{{QStringLiteral("profile")}, i18nc("@info:shell", "Name of profile to use for new Konsole instance"), QStringLiteral("name")},
|
||||
{{QStringLiteral("layout")}, i18nc("@info:shell", "json layoutfile to be loaded to use for new Konsole instance"), QStringLiteral("file")},
|
||||
{{QStringLiteral("builtin-profile")}, i18nc("@info:shell", "Use the built-in profile instead of the default profile")},
|
||||
{{QStringLiteral("workdir")}, i18nc("@info:shell", "Set the initial working directory of the new tab or window to 'dir'"), QStringLiteral("dir")},
|
||||
{{QStringLiteral("hold"), QStringLiteral("noclose")}, i18nc("@info:shell", "Do not close the initial session automatically when it ends.")},
|
||||
// BR: 373440
|
||||
{{QStringLiteral("new-tab")},
|
||||
i18nc("@info:shell",
|
||||
"Create a new tab in an existing window rather than creating a new window ('Run all Konsole windows in a single process' must be enabled)")},
|
||||
{{QStringLiteral("tabs-from-file")}, i18nc("@info:shell", "Create tabs as specified in given tabs configuration file"), QStringLiteral("file")},
|
||||
{{QStringLiteral("background-mode")},
|
||||
i18nc("@info:shell", "Start Konsole in the background and bring to the front when Ctrl+Shift+F12 (by default) is pressed")},
|
||||
{{QStringLiteral("separate"), QStringLiteral("nofork")}, i18nc("@info:shell", "Run in a separate process")},
|
||||
{{QStringLiteral("show-menubar")}, i18nc("@info:shell", "Show the menubar, overriding the default setting")},
|
||||
{{QStringLiteral("hide-menubar")}, i18nc("@info:shell", "Hide the menubar, overriding the default setting")},
|
||||
{{QStringLiteral("show-tabbar")}, i18nc("@info:shell", "Show the tabbar, overriding the default setting")},
|
||||
{{QStringLiteral("hide-tabbar")}, i18nc("@info:shell", "Hide the tabbar, overriding the default setting")},
|
||||
{{QStringLiteral("fullscreen")}, i18nc("@info:shell", "Start Konsole in fullscreen mode")},
|
||||
{{QStringLiteral("notransparency")}, i18nc("@info:shell", "Disable transparent backgrounds, even if the system supports them.")},
|
||||
{{QStringLiteral("list-profiles")}, i18nc("@info:shell", "List the available profiles")},
|
||||
{{QStringLiteral("list-profile-properties")}, i18nc("@info:shell", "List all the profile properties names and their type (for use with -p)")},
|
||||
{{QStringLiteral("p")}, i18nc("@info:shell", "Change the value of a profile property."), QStringLiteral("property=value")},
|
||||
{{QStringLiteral("e")},
|
||||
i18nc("@info:shell", "Command to execute. This option will catch all following arguments, so use it as the last option."),
|
||||
QStringLiteral("cmd")},
|
||||
{{QStringLiteral("force-reuse")},
|
||||
i18nc("@info:shell", "Force re-using the existing instance even if it breaks functionality, e. g. --new-tab. Mostly for debugging.")},
|
||||
};
|
||||
|
||||
for (const auto &option : options) {
|
||||
parser->addOption(option);
|
||||
}
|
||||
|
||||
parser->addPositionalArgument(QStringLiteral("[args]"), i18nc("@info:shell", "Arguments passed to command"));
|
||||
|
||||
// Add a no-op compatibility option to make Konsole compatible with
|
||||
// Debian's policy on X terminal emulators.
|
||||
// -T is technically meant to set a title, that is not really meaningful
|
||||
// for Konsole as we have multiple user-facing options controlling
|
||||
// the title and overriding whatever is set elsewhere.
|
||||
// https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=532029
|
||||
// https://www.debian.org/doc/debian-policy/ch-customized-programs.html#s11.8.3
|
||||
// --title is used by the VirtualBox Guest Additions installer
|
||||
auto titleOption =
|
||||
QCommandLineOption({QStringLiteral("T"), QStringLiteral("title")}, QStringLiteral("Debian policy compatibility, not used"), QStringLiteral("value"));
|
||||
titleOption.setFlags(QCommandLineOption::HiddenFromHelp);
|
||||
parser->addOption(titleOption);
|
||||
}
|
||||
|
||||
QStringList Application::getCustomCommand(QStringList &args)
|
||||
{
|
||||
int i = args.indexOf(QStringLiteral("-e"));
|
||||
QStringList customCommand;
|
||||
if ((0 < i) && (i < (args.size() - 1))) {
|
||||
// -e was specified with at least one extra argument
|
||||
// if -e was specified without arguments, QCommandLineParser will deal
|
||||
// with that
|
||||
args.removeAt(i);
|
||||
while (args.size() > i) {
|
||||
customCommand << args.takeAt(i);
|
||||
}
|
||||
}
|
||||
return customCommand;
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
SessionManager::instance()->closeAllSessions();
|
||||
}
|
||||
|
||||
MainWindow *Application::newMainWindow()
|
||||
{
|
||||
WindowSystemInfo::HAVE_TRANSPARENCY = !m_parser->isSet(QStringLiteral("notransparency"));
|
||||
|
||||
auto window = new MainWindow();
|
||||
|
||||
connect(window, &Konsole::MainWindow::newWindowRequest, this, &Konsole::Application::createWindow);
|
||||
|
||||
connect(window,
|
||||
&Konsole::MainWindow::terminalsDetached,
|
||||
this,
|
||||
[this, window](ViewSplitter *splitter, const QHash<TerminalDisplay *, Session *> &sessionsMap) {
|
||||
detachTerminals(window, splitter, sessionsMap);
|
||||
});
|
||||
|
||||
m_pluginManager.registerMainWindow(window);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
void Application::createWindow(const Profile::Ptr &profile, const QString &directory)
|
||||
{
|
||||
MainWindow *window = newMainWindow();
|
||||
window->createSession(profile, directory);
|
||||
window->show();
|
||||
}
|
||||
|
||||
void Application::detachTerminals(MainWindow *currentWindow, ViewSplitter *splitter, const QHash<TerminalDisplay *, Session *> &sessionsMap)
|
||||
{
|
||||
MainWindow *window = newMainWindow();
|
||||
ViewManager *manager = window->viewManager();
|
||||
|
||||
const QList<TerminalDisplay *> displays = splitter->findChildren<TerminalDisplay *>();
|
||||
for (TerminalDisplay *terminal : displays) {
|
||||
manager->attachView(terminal, sessionsMap[terminal]);
|
||||
}
|
||||
manager->activeContainer()->addSplitter(splitter);
|
||||
|
||||
window->show();
|
||||
window->resize(currentWindow->width(), currentWindow->height());
|
||||
window->move(QCursor::pos());
|
||||
}
|
||||
|
||||
int Application::newInstance()
|
||||
{
|
||||
// handle session management
|
||||
|
||||
// returns from processWindowArgs(args, createdNewMainWindow)
|
||||
// if a new window was created
|
||||
bool createdNewMainWindow = false;
|
||||
|
||||
// check for arguments to print help or other information to the
|
||||
// terminal, quit if such an argument was found
|
||||
if (processHelpArgs()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// create a new window or use an existing one
|
||||
MainWindow *window = processWindowArgs(createdNewMainWindow);
|
||||
|
||||
if (m_parser->isSet(QStringLiteral("tabs-from-file"))) {
|
||||
// create new session(s) as described in file
|
||||
if (!processTabsFromFileArgs(window)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// select profile to use
|
||||
Profile::Ptr baseProfile = processProfileSelectArgs();
|
||||
|
||||
// process various command-line options which cause a property of the
|
||||
// selected profile to be changed
|
||||
Profile::Ptr newProfile = processProfileChangeArgs(baseProfile);
|
||||
|
||||
// if layout file is enable load it and create session from definitions,
|
||||
// else create new session
|
||||
if (m_parser->isSet(QStringLiteral("layout"))) {
|
||||
window->viewManager()->loadLayout(m_parser->value(QStringLiteral("layout")));
|
||||
} else {
|
||||
Session *session = window->createSession(newProfile, QString());
|
||||
|
||||
const QString workingDir = m_parser->value(QStringLiteral("workdir"));
|
||||
if (!workingDir.isEmpty()) {
|
||||
session->setInitialWorkingDirectory(workingDir);
|
||||
}
|
||||
|
||||
if (m_parser->isSet(QStringLiteral("noclose"))) {
|
||||
session->setAutoClose(false);
|
||||
}
|
||||
}
|
||||
|
||||
// if the background-mode argument is supplied, start the background
|
||||
// session ( or bring to the front if it already exists )
|
||||
if (m_parser->isSet(QStringLiteral("background-mode"))) {
|
||||
startBackgroundMode(window);
|
||||
} else {
|
||||
// Qt constrains top-level windows which have not been manually
|
||||
// resized (via QWidget::resize()) to a maximum of 2/3rds of the
|
||||
// screen size.
|
||||
//
|
||||
// This means that the terminal display might not get the width/
|
||||
// height it asks for. To work around this, the widget must be
|
||||
// manually resized to its sizeHint().
|
||||
//
|
||||
// This problem only affects the first time the application is run.
|
||||
// run. After that KMainWindow will have manually resized the
|
||||
// window to its saved size at this point (so the Qt::WA_Resized
|
||||
// attribute will be set)
|
||||
|
||||
// If not restoring size from last time or only adding new tab,
|
||||
// resize window to chosen profile size (see Bug:345403)
|
||||
if (createdNewMainWindow) {
|
||||
QTimer::singleShot(0, window, &MainWindow::show);
|
||||
} else {
|
||||
window->setWindowState(window->windowState() & (~Qt::WindowMinimized | Qt::WindowActive));
|
||||
window->show();
|
||||
window->activateWindow();
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Documentation for tab file:
|
||||
*
|
||||
* ;; is the token separator
|
||||
* # at the beginning of line results in line being ignored.
|
||||
* supported tokens: title, command, profile and workdir
|
||||
*
|
||||
* Note that the title is static and the tab will close when the
|
||||
* command is complete (do not use --noclose). You can start new tabs.
|
||||
*
|
||||
* Example below will create 6 tabs as listed and a 7th default tab
|
||||
title: This is the title;; command: ssh localhost
|
||||
title: This is the title;; command: ssh localhost;; profile: Shell
|
||||
title: Top this!;; command: top
|
||||
title: mc this!;; command: mc;; workdir: /tmp
|
||||
#this line is comment
|
||||
command: ssh localhost
|
||||
profile: Shell
|
||||
*/
|
||||
bool Application::processTabsFromFileArgs(MainWindow *window)
|
||||
{
|
||||
// Open tab configuration file
|
||||
const QString tabsFileName(m_parser->value(QStringLiteral("tabs-from-file")));
|
||||
QFile tabsFile(tabsFileName);
|
||||
if (!tabsFile.open(QFile::ReadOnly)) {
|
||||
qWarning() << "ERROR: Cannot open tabs file " << tabsFileName.toLocal8Bit().data();
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int sessions = 0;
|
||||
while (!tabsFile.atEnd()) {
|
||||
QString lineString(QString::fromUtf8(tabsFile.readLine()).trimmed());
|
||||
if ((lineString.isEmpty()) || (lineString[0] == QLatin1Char('#'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QHash<QString, QString> lineTokens;
|
||||
QStringList lineParts = lineString.split(QStringLiteral(";;"), Qt::SkipEmptyParts);
|
||||
|
||||
for (int i = 0; i < lineParts.size(); ++i) {
|
||||
QString key = lineParts.at(i).section(QLatin1Char(':'), 0, 0).trimmed().toLower();
|
||||
QString value = lineParts.at(i).section(QLatin1Char(':'), 1, -1).trimmed();
|
||||
lineTokens[key] = value;
|
||||
}
|
||||
// should contain at least one of 'command' and 'profile'
|
||||
if (lineTokens.contains(QStringLiteral("command")) || lineTokens.contains(QStringLiteral("profile"))) {
|
||||
createTabFromArgs(window, lineTokens);
|
||||
sessions++;
|
||||
} else {
|
||||
qWarning() << "Each line should contain at least one of 'command' and 'profile'.";
|
||||
}
|
||||
}
|
||||
tabsFile.close();
|
||||
|
||||
if (sessions < 1) {
|
||||
qWarning() << "No valid lines found in " << tabsFileName.toLocal8Bit().data();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::createTabFromArgs(MainWindow *window, const QHash<QString, QString> &tokens)
|
||||
{
|
||||
const QString &title = tokens[QStringLiteral("title")];
|
||||
const QString &command = tokens[QStringLiteral("command")];
|
||||
const QString &profile = tokens[QStringLiteral("profile")];
|
||||
const QColor &color = tokens[QStringLiteral("tabcolor")];
|
||||
|
||||
Profile::Ptr baseProfile;
|
||||
if (!profile.isEmpty()) {
|
||||
baseProfile = ProfileManager::instance()->loadProfile(profile);
|
||||
}
|
||||
if (!baseProfile) {
|
||||
// fallback to default profile
|
||||
baseProfile = ProfileManager::instance()->defaultProfile();
|
||||
}
|
||||
|
||||
Profile::Ptr newProfile = Profile::Ptr(new Profile(baseProfile));
|
||||
newProfile->setHidden(true);
|
||||
|
||||
// FIXME: the method of determining whether to use newProfile does not
|
||||
// scale well when we support more fields in the future
|
||||
bool shouldUseNewProfile = false;
|
||||
|
||||
if (!command.isEmpty()) {
|
||||
newProfile->setProperty(Profile::Command, command);
|
||||
newProfile->setProperty(Profile::Arguments, command.split(QLatin1Char(' ')));
|
||||
shouldUseNewProfile = true;
|
||||
}
|
||||
|
||||
if (!title.isEmpty()) {
|
||||
newProfile->setProperty(Profile::LocalTabTitleFormat, title);
|
||||
newProfile->setProperty(Profile::RemoteTabTitleFormat, title);
|
||||
shouldUseNewProfile = true;
|
||||
}
|
||||
|
||||
// For tab color support
|
||||
if (color.isValid()) {
|
||||
newProfile->setProperty(Profile::TabColor, color);
|
||||
shouldUseNewProfile = true;
|
||||
}
|
||||
|
||||
// Create the new session
|
||||
Profile::Ptr theProfile = shouldUseNewProfile ? newProfile : baseProfile;
|
||||
Session *session = window->createSession(theProfile, QString());
|
||||
|
||||
const QString wdirOptionName(QStringLiteral("workdir"));
|
||||
auto it = tokens.constFind(wdirOptionName);
|
||||
const QString workingDirectory = it != tokens.cend() ? it.value() : m_parser->value(wdirOptionName);
|
||||
|
||||
if (!workingDirectory.isEmpty()) {
|
||||
session->setInitialWorkingDirectory(workingDirectory);
|
||||
}
|
||||
|
||||
if (m_parser->isSet(QStringLiteral("noclose"))) {
|
||||
session->setAutoClose(false);
|
||||
}
|
||||
|
||||
if (!window->testAttribute(Qt::WA_Resized)) {
|
||||
window->resize(window->sizeHint());
|
||||
}
|
||||
|
||||
// FIXME: this ugly hack here is to make the session start running, so that
|
||||
// its tab title is displayed as expected.
|
||||
//
|
||||
// This is another side effect of the commit fixing BKO 176902.
|
||||
window->show();
|
||||
window->hide();
|
||||
}
|
||||
|
||||
// Creates a new Konsole window.
|
||||
// If --new-tab is given, use existing window.
|
||||
MainWindow *Application::processWindowArgs(bool &createdNewMainWindow)
|
||||
{
|
||||
MainWindow *window = nullptr;
|
||||
|
||||
if (m_parser->isSet(QStringLiteral("new-tab"))) {
|
||||
const QList<QWidget *> list = QApplication::topLevelWidgets();
|
||||
for (auto it = list.crbegin(), endIt = list.crend(); it != endIt; ++it) {
|
||||
window = qobject_cast<MainWindow *>(*it);
|
||||
if (window) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (window == nullptr) {
|
||||
createdNewMainWindow = true;
|
||||
window = newMainWindow();
|
||||
|
||||
// override default menubar visibility
|
||||
if (m_parser->isSet(QStringLiteral("show-menubar"))) {
|
||||
window->setMenuBarInitialVisibility(true);
|
||||
}
|
||||
if (m_parser->isSet(QStringLiteral("hide-menubar"))) {
|
||||
window->setMenuBarInitialVisibility(false);
|
||||
}
|
||||
if (m_parser->isSet(QStringLiteral("fullscreen"))) {
|
||||
window->viewFullScreen(true);
|
||||
}
|
||||
if (m_parser->isSet(QStringLiteral("show-tabbar"))) {
|
||||
window->viewManager()->setNavigationVisibility(ViewManager::AlwaysShowNavigation);
|
||||
} else if (m_parser->isSet(QStringLiteral("hide-tabbar"))) {
|
||||
window->viewManager()->setNavigationVisibility(ViewManager::AlwaysHideNavigation);
|
||||
}
|
||||
}
|
||||
return window;
|
||||
}
|
||||
|
||||
// Loads a profile.
|
||||
// If --profile <name> is given, loads profile <name>.
|
||||
// If --builtin-profile is given, loads built-in profile.
|
||||
// Else loads the default profile.
|
||||
Profile::Ptr Application::processProfileSelectArgs()
|
||||
{
|
||||
if (m_parser->isSet(QStringLiteral("profile"))) {
|
||||
Profile::Ptr profile = ProfileManager::instance()->loadProfile(m_parser->value(QStringLiteral("profile")));
|
||||
if (profile) {
|
||||
return profile;
|
||||
}
|
||||
} else if (m_parser->isSet(QStringLiteral("builtin-profile"))) {
|
||||
// no need to check twice: built-in and default profiles are always available
|
||||
return ProfileManager::instance()->builtinProfile();
|
||||
}
|
||||
return ProfileManager::instance()->defaultProfile();
|
||||
}
|
||||
|
||||
bool Application::processHelpArgs()
|
||||
{
|
||||
if (m_parser->isSet(QStringLiteral("list-profiles"))) {
|
||||
listAvailableProfiles();
|
||||
return true;
|
||||
} else if (m_parser->isSet(QStringLiteral("list-profile-properties"))) {
|
||||
listProfilePropertyInfo();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Application::listAvailableProfiles()
|
||||
{
|
||||
const QStringList paths = ProfileManager::instance()->availableProfilePaths();
|
||||
|
||||
for (const QString &path : paths) {
|
||||
QFileInfo info(path);
|
||||
printf("%s\n", info.completeBaseName().toLocal8Bit().constData());
|
||||
}
|
||||
}
|
||||
|
||||
void Application::listProfilePropertyInfo()
|
||||
{
|
||||
const std::vector<std::string> &properties = Profile::propertiesInfoList();
|
||||
|
||||
for (const auto &prop : properties) {
|
||||
printf("%s\n", prop.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
Profile::Ptr Application::processProfileChangeArgs(Profile::Ptr baseProfile)
|
||||
{
|
||||
bool shouldUseNewProfile = false;
|
||||
|
||||
Profile::Ptr newProfile = Profile::Ptr(new Profile(baseProfile));
|
||||
newProfile->setHidden(true);
|
||||
|
||||
// temporary changes to profile options specified on the command line
|
||||
const QStringList profileProperties = m_parser->values(QStringLiteral("p"));
|
||||
for (const QString &value : profileProperties) {
|
||||
ProfileCommandParser parser;
|
||||
newProfile->assignProperties(parser.parse(value));
|
||||
shouldUseNewProfile = true;
|
||||
}
|
||||
|
||||
// run a custom command
|
||||
if (!m_customCommand.isEmpty()) {
|
||||
// Example: konsole -e man ls
|
||||
QString commandExec = m_customCommand[0];
|
||||
QStringList commandArguments(m_customCommand);
|
||||
if ((m_customCommand.size() == 1) && (QStandardPaths::findExecutable(commandExec).isEmpty())) {
|
||||
// Example: konsole -e "man ls"
|
||||
ShellCommand shellCommand(commandExec);
|
||||
commandExec = shellCommand.command();
|
||||
commandArguments = shellCommand.arguments();
|
||||
}
|
||||
|
||||
if (commandExec.startsWith(QLatin1String("./"))) {
|
||||
commandExec = QDir::currentPath() + commandExec.mid(1);
|
||||
}
|
||||
|
||||
newProfile->setProperty(Profile::Command, commandExec);
|
||||
newProfile->setProperty(Profile::Arguments, commandArguments);
|
||||
|
||||
shouldUseNewProfile = true;
|
||||
}
|
||||
|
||||
if (shouldUseNewProfile) {
|
||||
return newProfile;
|
||||
}
|
||||
return baseProfile;
|
||||
}
|
||||
|
||||
void Application::startBackgroundMode(MainWindow *window)
|
||||
{
|
||||
if (_backgroundInstance != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if HAVE_DBUS
|
||||
KActionCollection *collection = window->actionCollection();
|
||||
QAction *action = collection->addAction(QStringLiteral("toggle-background-window"));
|
||||
action->setObjectName(QStringLiteral("Konsole Background Mode"));
|
||||
action->setText(i18nc("@item", "Toggle Background Window"));
|
||||
KGlobalAccel::self()->setGlobalShortcut(action, QKeySequence(Konsole::ACCEL | Qt::Key_F12));
|
||||
connect(action, &QAction::triggered, this, &Application::toggleBackgroundInstance);
|
||||
#endif
|
||||
_backgroundInstance = window;
|
||||
}
|
||||
|
||||
void Application::toggleBackgroundInstance()
|
||||
{
|
||||
Q_ASSERT(_backgroundInstance);
|
||||
|
||||
if (!_backgroundInstance->isVisible()) {
|
||||
_backgroundInstance->show();
|
||||
// ensure that the active terminal display has the focus. Without
|
||||
// this, an odd problem occurred where the focus widget would change
|
||||
// each time the background instance was shown
|
||||
_backgroundInstance->setFocus();
|
||||
} else {
|
||||
_backgroundInstance->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::slotActivateRequested(QStringList args, const QString & /*workingDir*/)
|
||||
{
|
||||
// QCommandLineParser expects the first argument to be the executable name
|
||||
// In the current version it just strips it away
|
||||
args.prepend(qApp->applicationFilePath());
|
||||
|
||||
m_customCommand = getCustomCommand(args);
|
||||
|
||||
// We can't re-use QCommandLineParser instances, it preserves earlier parsed values
|
||||
auto parser = new QCommandLineParser;
|
||||
populateCommandLineParser(parser);
|
||||
parser->parse(args);
|
||||
m_parser.reset(parser);
|
||||
|
||||
newInstance();
|
||||
}
|
||||
|
||||
#include "moc_Application.cpp"
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef APPLICATION_H
|
||||
#define APPLICATION_H
|
||||
|
||||
// Qt
|
||||
#include <QCommandLineParser>
|
||||
|
||||
// Konsole
|
||||
#include "konsoleapp_export.h"
|
||||
#include "pluginsystem/PluginManager.h"
|
||||
#include "profile/Profile.h"
|
||||
#include "terminalDisplay/TerminalDisplay.h"
|
||||
#include "widgets/ViewSplitter.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class MainWindow;
|
||||
class Session;
|
||||
class Profile;
|
||||
|
||||
/**
|
||||
* The Konsole Application.
|
||||
*
|
||||
* The application consists of one or more main windows and a set of
|
||||
* factories to create new sessions and views.
|
||||
*
|
||||
* To create a new main window with a default terminal session, call
|
||||
* the newInstance(). Empty main windows can be created using newMainWindow().
|
||||
*
|
||||
* The factory used to create new terminal sessions can be retrieved using
|
||||
* the sessionManager() accessor.
|
||||
*/
|
||||
class KONSOLEAPP_EXPORT Application : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/** Constructs a new Konsole application. */
|
||||
explicit Application(QSharedPointer<QCommandLineParser> parser, const QStringList &customCommand);
|
||||
|
||||
static void populateCommandLineParser(QCommandLineParser *parser);
|
||||
static QStringList getCustomCommand(QStringList &args);
|
||||
|
||||
~Application() override;
|
||||
|
||||
/** Creates a new main window and opens a default terminal session */
|
||||
int newInstance();
|
||||
|
||||
/**
|
||||
* Creates a new, empty main window and connects to its newSessionRequest()
|
||||
* and newWindowRequest() signals to trigger creation of new sessions or
|
||||
* windows when then they are emitted.
|
||||
*/
|
||||
MainWindow *newMainWindow();
|
||||
|
||||
private Q_SLOTS:
|
||||
void createWindow(const QExplicitlySharedDataPointer<Profile> &profile, const QString &directory);
|
||||
void detachTerminals(MainWindow *currentWindow, ViewSplitter *splitter, const QHash<TerminalDisplay *, Session *> &sessionsMap);
|
||||
|
||||
void toggleBackgroundInstance();
|
||||
|
||||
public Q_SLOTS:
|
||||
void slotActivateRequested(QStringList args, const QString &workingDir);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(Application)
|
||||
|
||||
void listAvailableProfiles();
|
||||
void listProfilePropertyInfo();
|
||||
void startBackgroundMode(MainWindow *window);
|
||||
bool processHelpArgs();
|
||||
MainWindow *processWindowArgs(bool &createdNewMainWindow);
|
||||
QExplicitlySharedDataPointer<Profile> processProfileSelectArgs();
|
||||
QExplicitlySharedDataPointer<Profile> processProfileChangeArgs(QExplicitlySharedDataPointer<Profile> baseProfile);
|
||||
bool processTabsFromFileArgs(MainWindow *window);
|
||||
void createTabFromArgs(MainWindow *window, const QHash<QString, QString> &);
|
||||
|
||||
MainWindow *_backgroundInstance;
|
||||
QSharedPointer<QCommandLineParser> m_parser;
|
||||
QStringList m_customCommand;
|
||||
PluginManager m_pluginManager;
|
||||
};
|
||||
}
|
||||
#endif // APPLICATION_H
|
||||
@@ -0,0 +1,166 @@
|
||||
/* This file was part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2002 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Born as kdelibs/kio/kfile/kfilebookmarkhandler.cpp
|
||||
|
||||
// Own
|
||||
#include "BookmarkHandler.h"
|
||||
|
||||
// Qt
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QStandardPaths>
|
||||
|
||||
// KDE
|
||||
#include <KLocalizedString>
|
||||
#include <KShell>
|
||||
|
||||
// Konsole
|
||||
#include "BookmarkMenu.h"
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
BookmarkHandler::BookmarkHandler(KActionCollection *collection, QMenu *menu, bool toplevel, QObject *parent)
|
||||
: QObject(parent)
|
||||
, KBookmarkOwner()
|
||||
, _menu(menu)
|
||||
, _file(QString())
|
||||
, _toplevel(toplevel)
|
||||
, _activeView(nullptr)
|
||||
, _views(QList<ViewProperties *>())
|
||||
{
|
||||
setObjectName(QStringLiteral("BookmarkHandler"));
|
||||
|
||||
_file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("konsole/bookmarks.xml"));
|
||||
|
||||
if (_file.isEmpty()) {
|
||||
_file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/konsole");
|
||||
QDir().mkpath(_file);
|
||||
_file += QStringLiteral("/bookmarks.xml");
|
||||
}
|
||||
|
||||
KBookmarkManager *manager = new KBookmarkManager(_file, this);
|
||||
|
||||
// This constructor is only called with toplevel as true; regardless the
|
||||
// fourth argument can not be nullptr as it will crash in BookmarkMenu
|
||||
// TODO: remove the toplevel boolean argument in API
|
||||
auto *bookmarkMenu = new BookmarkMenu(manager, this, _menu, collection);
|
||||
bookmarkMenu->setParent(this);
|
||||
}
|
||||
|
||||
BookmarkHandler::~BookmarkHandler() = default;
|
||||
|
||||
void BookmarkHandler::openBookmark(const KBookmark &bm, Qt::MouseButtons, Qt::KeyboardModifiers)
|
||||
{
|
||||
Q_EMIT openUrl(bm.url());
|
||||
}
|
||||
|
||||
void BookmarkHandler::openFolderinTabs(const KBookmarkGroup &group)
|
||||
{
|
||||
Q_EMIT openUrls(group.groupUrlList());
|
||||
}
|
||||
|
||||
bool BookmarkHandler::enableOption(BookmarkOption option) const
|
||||
{
|
||||
if (option == ShowAddBookmark || option == ShowEditBookmark) {
|
||||
return _toplevel;
|
||||
}
|
||||
return KBookmarkOwner::enableOption(option);
|
||||
}
|
||||
|
||||
QUrl BookmarkHandler::currentUrl() const
|
||||
{
|
||||
return urlForView(_activeView);
|
||||
}
|
||||
|
||||
QUrl BookmarkHandler::urlForView(ViewProperties *view) const
|
||||
{
|
||||
if (view != nullptr) {
|
||||
return view->url();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString BookmarkHandler::currentTitle() const
|
||||
{
|
||||
return titleForView(_activeView);
|
||||
}
|
||||
|
||||
QString BookmarkHandler::titleForView(ViewProperties *view) const
|
||||
{
|
||||
const QUrl &url = view != nullptr ? view->url() : QUrl();
|
||||
if (url.isLocalFile()) {
|
||||
QString path = url.path();
|
||||
|
||||
path = KShell::tildeExpand(path);
|
||||
path = QFileInfo(path).completeBaseName();
|
||||
|
||||
return path;
|
||||
}
|
||||
if (!url.host().isEmpty()) {
|
||||
if (!url.userName().isEmpty()) {
|
||||
return i18nc("@item:inmenu The user's name and host they are connected to via ssh", "%1 on %2", url.userName(), url.host());
|
||||
}
|
||||
return i18nc("@item:inmenu The host the user is connected to via ssh", "%1", url.host());
|
||||
}
|
||||
|
||||
return url.toDisplayString();
|
||||
}
|
||||
|
||||
QString BookmarkHandler::currentIcon() const
|
||||
{
|
||||
return iconForView(_activeView);
|
||||
}
|
||||
|
||||
QString BookmarkHandler::iconForView(ViewProperties *view) const
|
||||
{
|
||||
if (view != nullptr) {
|
||||
return view->icon().name();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QList<KBookmarkOwner::FutureBookmark> BookmarkHandler::currentBookmarkList() const
|
||||
{
|
||||
QList<KBookmarkOwner::FutureBookmark> list;
|
||||
list.reserve(_views.size());
|
||||
|
||||
const QList<ViewProperties *> views = _views;
|
||||
for (ViewProperties *view : views) {
|
||||
list << KBookmarkOwner::FutureBookmark(titleForView(view), urlForView(view), iconForView(view));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
bool BookmarkHandler::supportsTabs() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void BookmarkHandler::setViews(const QList<ViewProperties *> &views)
|
||||
{
|
||||
_views = views;
|
||||
}
|
||||
|
||||
QList<ViewProperties *> BookmarkHandler::views() const
|
||||
{
|
||||
return _views;
|
||||
}
|
||||
|
||||
void BookmarkHandler::setActiveView(ViewProperties *view)
|
||||
{
|
||||
_activeView = view;
|
||||
}
|
||||
|
||||
ViewProperties *BookmarkHandler::activeView() const
|
||||
{
|
||||
return _activeView;
|
||||
}
|
||||
|
||||
#include "moc_BookmarkHandler.cpp"
|
||||
@@ -0,0 +1,118 @@
|
||||
/* This file was part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2002 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Born as kdelibs/kio/kfile/kfilebookmarkhandler.h
|
||||
|
||||
#ifndef BOOKMARKHANDLER_H
|
||||
#define BOOKMARKHANDLER_H
|
||||
|
||||
// KDE
|
||||
#include <KBookmarkManager>
|
||||
#include <KBookmarkOwner>
|
||||
|
||||
// Konsole
|
||||
#include "ViewProperties.h"
|
||||
#include "konsoleprivate_export.h"
|
||||
|
||||
class QMenu;
|
||||
class QUrl;
|
||||
class KActionCollection;
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
|
||||
/**
|
||||
* This class handles the communication between the bookmark menu and the active session,
|
||||
* providing a suggested title and URL when the user clicks the "Add Bookmark" item in
|
||||
* the bookmarks menu.
|
||||
*
|
||||
* The bookmark handler is associated with a session controller, which is used to
|
||||
* determine the working URL of the current session. When the user changes the active
|
||||
* view, the bookmark handler's controller should be changed using setController()
|
||||
*
|
||||
* When the user selects a bookmark, the openUrl() signal is emitted.
|
||||
*/
|
||||
class KONSOLEPRIVATE_EXPORT BookmarkHandler : public QObject, public KBookmarkOwner
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a new bookmark handler for Konsole bookmarks.
|
||||
*
|
||||
* @param collection The collection which the bookmark menu's actions should be added to
|
||||
* @param menu The menu which the bookmark actions should be added to
|
||||
* @param toplevel TODO: Document me
|
||||
* @param parent The parent object
|
||||
*/
|
||||
BookmarkHandler(KActionCollection *collection, QMenu *menu, bool toplevel, QObject *parent);
|
||||
~BookmarkHandler() override;
|
||||
|
||||
QUrl currentUrl() const override;
|
||||
QString currentTitle() const override;
|
||||
QString currentIcon() const override;
|
||||
bool enableOption(BookmarkOption option) const override;
|
||||
bool supportsTabs() const override;
|
||||
QList<KBookmarkOwner::FutureBookmark> currentBookmarkList() const override;
|
||||
void openFolderinTabs(const KBookmarkGroup &group) override;
|
||||
|
||||
/**
|
||||
* Returns the menu which this bookmark handler inserts its actions into.
|
||||
*/
|
||||
QMenu *menu() const
|
||||
{
|
||||
return _menu;
|
||||
}
|
||||
|
||||
QList<ViewProperties *> views() const;
|
||||
ViewProperties *activeView() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void setViews(const QList<ViewProperties *> &views);
|
||||
|
||||
void setActiveView(ViewProperties *view);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when the user selects a bookmark from the bookmark menu.
|
||||
*
|
||||
* @param url The url of the bookmark which was selected by the user.
|
||||
*/
|
||||
void openUrl(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Emitted when the user selects 'Open Folder in Tabs'
|
||||
* from the bookmark menu.
|
||||
*
|
||||
* @param urls The urls of the bookmarks in the folder whose
|
||||
* 'Open Folder in Tabs' action was triggered
|
||||
*/
|
||||
void openUrls(const QList<QUrl> &urls);
|
||||
|
||||
private Q_SLOTS:
|
||||
void openBookmark(const KBookmark &bm, Qt::MouseButtons, Qt::KeyboardModifiers) override;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(BookmarkHandler)
|
||||
|
||||
QString titleForView(ViewProperties *view) const;
|
||||
QUrl urlForView(ViewProperties *view) const;
|
||||
QString iconForView(ViewProperties *view) const;
|
||||
|
||||
QMenu *_menu;
|
||||
QString _file;
|
||||
bool _toplevel;
|
||||
ViewProperties *_activeView;
|
||||
QList<ViewProperties *> _views;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BOOKMARKHANDLER_H
|
||||
@@ -0,0 +1,65 @@
|
||||
/* This file was part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2019 Tomaz Canabrava <tcanabrava@kde.org>
|
||||
SPDX-FileCopyrightText: 2019 Martin Sandsmark <martin.sandsmark@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "BookmarkMenu.h"
|
||||
|
||||
// KDE
|
||||
#include <KActionCollection>
|
||||
#include <KBookmark>
|
||||
#include <KBookmarkManager>
|
||||
#include <KBookmarkOwner>
|
||||
|
||||
// Qt
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
|
||||
#include <algorithm> // std::any_of
|
||||
|
||||
BookmarkMenu::BookmarkMenu(KBookmarkManager *mgr, KBookmarkOwner *owner, QMenu *parentMenu, KActionCollection *collection)
|
||||
: KBookmarkMenu(mgr, owner, parentMenu)
|
||||
{
|
||||
QAction *bookmarkAction;
|
||||
collection->addActions(parentMenu->actions());
|
||||
|
||||
bookmarkAction = addBookmarkAction();
|
||||
|
||||
Q_ASSERT(bookmarkAction);
|
||||
|
||||
// We need to hijack the action - note this only hijacks top-level action
|
||||
disconnect(bookmarkAction, nullptr, this, nullptr);
|
||||
connect(bookmarkAction, &QAction::triggered, this, &BookmarkMenu::maybeAddBookmark);
|
||||
|
||||
#ifndef Q_OS_MACOS // not needed on this platform, Cmd+B (shortcut) is different to Ctrl+B (^B in terminal)
|
||||
// replace Ctrl+B shortcut for bookmarks only if user hasn't already
|
||||
// changed the shortcut; however, if the user changed it to Ctrl+B
|
||||
// this will still get changed to Ctrl+Shift+B
|
||||
if (bookmarkAction->shortcut() == QKeySequence(Qt::CTRL | Qt::Key_B)) {
|
||||
collection->setDefaultShortcut(bookmarkAction, Qt::CTRL | Qt::SHIFT | Qt::Key_B);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void BookmarkMenu::maybeAddBookmark()
|
||||
{
|
||||
// Check for duplicates first
|
||||
// This only catches duplicates in top-level (ie not sub-folders);
|
||||
// this is due to only the top-level add_bookmark is hijacked.
|
||||
const KBookmarkGroup rootGroup = manager()->root();
|
||||
const QUrl currUrl = owner()->currentUrl();
|
||||
const auto urlList = rootGroup.groupUrlList();
|
||||
if (std::any_of(urlList.constBegin(), urlList.constEnd(), [currUrl](const QUrl &url) {
|
||||
return url == currUrl;
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
|
||||
slotAddBookmark();
|
||||
}
|
||||
|
||||
#include "moc_BookmarkMenu.cpp"
|
||||
@@ -0,0 +1,37 @@
|
||||
/* This file was part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2019 Tomaz Canabrava <tcanabrava@kde.org>
|
||||
SPDX-FileCopyrightText: 2019 Martin Sandsmark <martin.sandsmark@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BOOKMARKMENU_H
|
||||
#define BOOKMARKMENU_H
|
||||
|
||||
// KDE
|
||||
#include <KBookmarkMenu>
|
||||
|
||||
// Konsole
|
||||
#include "konsoleprivate_export.h"
|
||||
|
||||
class KActionCollection;
|
||||
|
||||
/* Hackish hack to mitigate a broken behavior of KBookmarkMenu.
|
||||
* slotAddBookmark accepts duplicates and it's fragile code,
|
||||
* that thing really deserves a rewrite.
|
||||
* the easiest way is to "hijack" it's protected method to public
|
||||
* and just cast around.
|
||||
*/
|
||||
class KONSOLEPRIVATE_EXPORT BookmarkMenu : public KBookmarkMenu
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BookmarkMenu(KBookmarkManager *mgr, KBookmarkOwner *owner, QMenu *parentMenu, KActionCollection *collection);
|
||||
|
||||
private Q_SLOTS:
|
||||
void maybeAddBookmark();
|
||||
};
|
||||
|
||||
#endif // BOOKMARKMENU_H
|
||||
@@ -0,0 +1,411 @@
|
||||
# cmake-options : -DCMAKE_DISABLE_FIND_PACKAGE_LibKonq=TRUE or FALSE; default is FALSE
|
||||
|
||||
add_definitions(-DTRANSLATION_DOMAIN=\"konsole\")
|
||||
|
||||
### Handle DragonFlyBSD here instead of using __DragonFly__
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "DragonFly")
|
||||
set(HAVE_OS_DRAGONFLYBSD 1)
|
||||
else()
|
||||
set(HAVE_OS_DRAGONFLYBSD 0)
|
||||
endif()
|
||||
|
||||
include(CheckIncludeFiles)
|
||||
include(ECMAddAppIcon)
|
||||
|
||||
configure_file(config-konsole.h.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/config-konsole.h)
|
||||
|
||||
### Tests
|
||||
if(BUILD_TESTING)
|
||||
find_package(Qt6Test ${QT_MIN_VERSION} CONFIG REQUIRED)
|
||||
add_subdirectory(autotests)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
|
||||
### Security concerns about sendText and runCommand dbus methods being public
|
||||
option(REMOVE_SENDTEXT_RUNCOMMAND_DBUS_METHODS "Konsole: remove sendText and runCommand dbus methods" OFF)
|
||||
|
||||
### Security concerns about reading arbitrary screen positions
|
||||
option(ENABLE_DECRQCRA "Konsole: enable DEC request checksum rectangular area" OFF)
|
||||
|
||||
### Development tools
|
||||
option(KONSOLE_BUILD_UNI2CHARACTERWIDTH "Konsole: build uni2characterwidth executable" OFF)
|
||||
|
||||
set(dbus_xml_srcs)
|
||||
if(HAVE_DBUS)
|
||||
### Konsole source files shared between embedded terminal and main application
|
||||
# qdbuscpp2xml -m Session.h -o org.kde.konsole.Session.xml
|
||||
# qdbuscpp2xml -M -s ViewManager.h -o org.kde.konsole.Konsole.xml
|
||||
|
||||
# Generate dbus .xml files; do not store .xml in source folder
|
||||
qt_generate_dbus_interface(ViewManager.h org.kde.konsole.Window.xml OPTIONS -m)
|
||||
|
||||
qt_add_dbus_adaptor(windowadaptors_SRCS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/org.kde.konsole.Window.xml
|
||||
ViewManager.h
|
||||
Konsole::ViewManager)
|
||||
|
||||
# qdbuscpp2xml -m Session.h -o org.kde.konsole.Session.xml
|
||||
# Generate dbus .xml files; do not store .xml in source folder
|
||||
qt_generate_dbus_interface(session/Session.h org.kde.konsole.Session.xml OPTIONS -m)
|
||||
|
||||
qt_add_dbus_adaptor(
|
||||
sessionadaptors_SRCS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/org.kde.konsole.Session.xml
|
||||
session/Session.h
|
||||
Konsole::Session
|
||||
)
|
||||
|
||||
set(dbus_xml_srcs
|
||||
${CMAKE_CURRENT_BINARY_DIR}/org.kde.konsole.Session.xml
|
||||
${CMAKE_CURRENT_BINARY_DIR}/org.kde.konsole.Window.xml
|
||||
)
|
||||
endif()
|
||||
|
||||
set(konsole_LIBS
|
||||
KF6::XmlGui
|
||||
Qt::Multimedia
|
||||
Qt::PrintSupport
|
||||
Qt::Xml
|
||||
KF6::Notifications
|
||||
KF6::WindowSystem
|
||||
KF6::TextWidgets
|
||||
KF6::GuiAddons
|
||||
KF6::IconThemes
|
||||
KF6::Bookmarks
|
||||
KF6::I18n
|
||||
KF6::KIOWidgets
|
||||
KF6::NewStuffCore
|
||||
)
|
||||
|
||||
if(NOT WIN32)
|
||||
list(APPEND konsole_LIBS
|
||||
KF6::Pty
|
||||
)
|
||||
endif()
|
||||
|
||||
if(HAVE_DBUS)
|
||||
list(APPEND konsole_LIBS
|
||||
KF6::DBusAddons
|
||||
KF6::GlobalAccel
|
||||
)
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
#kinfo_getfile() is in libutil
|
||||
list(APPEND konsole_LIBS util)
|
||||
endif()
|
||||
|
||||
# TODO: Move this to an internal folder if it grows too much.
|
||||
# This konsoleprivate_core is the "core" library that depends in nothing from konsole itself
|
||||
# so we can start to detangle the codebase.
|
||||
set(konsoleprivate_core_SRCS
|
||||
ShellCommand.cpp
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(
|
||||
konsoleprivate_core_SRCS
|
||||
HEADER konsoledebug.h
|
||||
IDENTIFIER KonsoleDebug
|
||||
CATEGORY_NAME org.kde.konsole
|
||||
DESCRIPTION "Konsole"
|
||||
EXPORT KONSOLE
|
||||
)
|
||||
|
||||
add_library(konsoleprivate_core STATIC ${konsoleprivate_core_SRCS})
|
||||
# Needed to link this static lib to shared libs
|
||||
set_target_properties(konsoleprivate_core PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
target_link_libraries(konsoleprivate_core KF6::CoreAddons)
|
||||
|
||||
set(konsolehelpers_SRCS
|
||||
LabelsAligner.cpp
|
||||
)
|
||||
add_library(konsolehelpers
|
||||
STATIC
|
||||
${konsolehelpers_SRCS}
|
||||
)
|
||||
set_target_properties(konsolehelpers PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
target_link_libraries(konsolehelpers
|
||||
Qt::Core
|
||||
Qt::Widgets
|
||||
)
|
||||
|
||||
add_subdirectory(colorscheme)
|
||||
add_subdirectory(keyboardtranslator)
|
||||
add_subdirectory(characters)
|
||||
add_subdirectory(decoders)
|
||||
add_subdirectory(plugins)
|
||||
if(WIN32)
|
||||
set(ptyqt_srcs ptyqt/iptyprocess.cpp ptyqt/conptyprocess.cpp)
|
||||
endif()
|
||||
|
||||
set(konsoleprivate_SRCS ${windowadaptors_SRCS}
|
||||
BookmarkHandler.cpp
|
||||
BookmarkMenu.cpp
|
||||
CheckableSessionModel.cpp
|
||||
CopyInputDialog.cpp
|
||||
Emulation.cpp
|
||||
EscapeSequenceUrlExtractor.cpp
|
||||
FontDialog.cpp
|
||||
HistorySizeDialog.cpp
|
||||
KeyBindingEditor.cpp
|
||||
LabelsAligner.cpp
|
||||
NullProcessInfo.cpp
|
||||
PrintOptions.cpp
|
||||
ProcessInfo.cpp
|
||||
Pty.cpp
|
||||
RenameTabDialog.cpp
|
||||
SSHProcessInfo.cpp
|
||||
SaveHistoryTask.cpp
|
||||
Screen.cpp
|
||||
ScreenWindow.cpp
|
||||
ScrollState.cpp
|
||||
SearchHistoryTask.cpp
|
||||
ShouldApplyProperty.cpp
|
||||
UnixProcessInfo.cpp
|
||||
ViewManager.cpp
|
||||
ViewProperties.cpp
|
||||
Vt102Emulation.cpp
|
||||
WindowSystemInfo.cpp
|
||||
ZModemDialog.cpp
|
||||
filterHotSpots/EscapeSequenceUrlFilter.cpp
|
||||
filterHotSpots/EscapeSequenceUrlFilterHotSpot.cpp
|
||||
filterHotSpots/FileFilter.cpp
|
||||
filterHotSpots/FileFilterHotspot.cpp
|
||||
filterHotSpots/Filter.cpp
|
||||
filterHotSpots/FilterChain.cpp
|
||||
filterHotSpots/HotSpot.cpp
|
||||
filterHotSpots/RegExpFilter.cpp
|
||||
filterHotSpots/RegExpFilterHotspot.cpp
|
||||
filterHotSpots/TerminalImageFilterChain.cpp
|
||||
filterHotSpots/UrlFilter.cpp
|
||||
filterHotSpots/UrlFilterHotspot.cpp
|
||||
filterHotSpots/ColorFilter.cpp
|
||||
filterHotSpots/ColorFilterHotSpot.cpp
|
||||
history/HistoryFile.cpp
|
||||
history/HistoryScroll.cpp
|
||||
history/HistoryScrollFile.cpp
|
||||
history/HistoryScrollNone.cpp
|
||||
history/HistoryType.cpp
|
||||
history/HistoryTypeFile.cpp
|
||||
history/HistoryTypeNone.cpp
|
||||
history/compact/CompactHistoryScroll.cpp
|
||||
history/compact/CompactHistoryType.cpp
|
||||
widgets/DetachableTabBar.cpp
|
||||
widgets/EditProfileDialog.cpp
|
||||
widgets/HistorySizeWidget.cpp
|
||||
widgets/IncrementalSearchBar.cpp
|
||||
widgets/RenameTabWidget.cpp
|
||||
widgets/TabTitleFormatButton.cpp
|
||||
|
||||
terminalDisplay/extras/CompositeWidgetFocusWatcher.cpp
|
||||
terminalDisplay/extras/AutoScrollHandler.cpp
|
||||
terminalDisplay/extras/HighlightScrolledLines.cpp
|
||||
|
||||
terminalDisplay/TerminalDisplay.cpp
|
||||
terminalDisplay/TerminalPainter.cpp
|
||||
terminalDisplay/TerminalScrollBar.cpp
|
||||
terminalDisplay/TerminalColor.cpp
|
||||
terminalDisplay/TerminalFonts.cpp
|
||||
terminalDisplay/TerminalBell.cpp
|
||||
|
||||
profile/Profile.cpp
|
||||
profile/ProfileCommandParser.cpp
|
||||
profile/ProfileGroup.cpp
|
||||
profile/ProfileList.cpp
|
||||
profile/ProfileReader.cpp
|
||||
profile/ProfileWriter.cpp
|
||||
profile/ProfileManager.cpp
|
||||
profile/ProfileModel.cpp
|
||||
|
||||
${ptyqt_srcs} # windows conpty backend
|
||||
|
||||
${sessionadaptors_SRCS}
|
||||
session/Session.cpp
|
||||
session/SessionController.cpp
|
||||
session/SessionDisplayConnection.cpp
|
||||
session/SessionGroup.cpp
|
||||
session/SessionListModel.cpp
|
||||
session/SessionManager.cpp
|
||||
session/SessionTask.cpp
|
||||
|
||||
widgets/TerminalDisplayAccessible.cpp
|
||||
widgets/TerminalHeaderBar.cpp
|
||||
widgets/ViewContainer.cpp
|
||||
widgets/ViewSplitter.cpp
|
||||
widgets/KonsolePrintManager.cpp
|
||||
|
||||
${dbus_xml_srcs}
|
||||
|
||||
../data/data.qrc
|
||||
../desktop/konsole.qrc)
|
||||
|
||||
kconfig_add_kcfg_files(konsoleprivate_SRCS settings/KonsoleSettings.kcfgc)
|
||||
|
||||
### Konsole Application
|
||||
|
||||
ki18n_wrap_ui(konsoleprivate_SRCS
|
||||
CopyInputDialog.ui
|
||||
HistorySizeDialog.ui
|
||||
KeyBindingEditor.ui
|
||||
PrintOptions.ui # Temporary to build session static library
|
||||
RenameTabDialog.ui
|
||||
settings/GeneralSettings.ui
|
||||
settings/MemorySettings.ui
|
||||
settings/PartInfo.ui
|
||||
settings/ProfileSettings.ui
|
||||
settings/TabBarSettings.ui
|
||||
settings/TemporaryFilesSettings.ui
|
||||
settings/ThumbnailsSettings.ui
|
||||
widgets/EditProfileAdvancedPage.ui
|
||||
widgets/EditProfileAppearancePage.ui
|
||||
widgets/EditProfileGeneralPage.ui
|
||||
widgets/EditProfileKeyboardPage.ui
|
||||
widgets/EditProfileMousePage.ui
|
||||
widgets/EditProfileScrollingPage.ui
|
||||
widgets/EditProfileTabsPage.ui
|
||||
widgets/HistorySizeWidget.ui
|
||||
widgets/RenameTabWidget.ui
|
||||
)
|
||||
|
||||
add_library(konsoleprivate ${konsoleprivate_SRCS})
|
||||
generate_export_header(konsoleprivate BASE_NAME konsoleprivate)
|
||||
|
||||
find_package(ZLIB)
|
||||
|
||||
target_link_libraries(konsoleprivate
|
||||
PUBLIC
|
||||
konsoleprivate_core
|
||||
konsolecolorscheme
|
||||
keyboardtranslator
|
||||
konsolehelpers
|
||||
konsolecharacters
|
||||
konsoledecoders
|
||||
KF6::NewStuffCore
|
||||
KF6::NewStuffWidgets
|
||||
${konsole_LIBS}
|
||||
ZLIB::ZLIB
|
||||
ICU::uc
|
||||
ICU::i18n
|
||||
)
|
||||
|
||||
target_link_libraries(konsoleprivate
|
||||
PUBLIC
|
||||
Qt6::Core5Compat
|
||||
PRIVATE
|
||||
KF6::IconWidgets
|
||||
KF6::BookmarksWidgets
|
||||
)
|
||||
|
||||
set_target_properties(konsoleprivate PROPERTIES
|
||||
VERSION ${RELEASE_SERVICE_VERSION}
|
||||
)
|
||||
|
||||
install(TARGETS konsoleprivate ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP)
|
||||
|
||||
add_library(konsoleapp SHARED Application.cpp
|
||||
MainWindow.cpp
|
||||
settings/ConfigurationDialog.cpp
|
||||
settings/ConfigDialogButtonGroupManager.cpp
|
||||
settings/TemporaryFilesSettings.cpp
|
||||
settings/GeneralSettings.cpp
|
||||
settings/MemorySettings.cpp
|
||||
settings/ProfileSettings.cpp
|
||||
settings/TabBarSettings.cpp
|
||||
settings/ThumbnailsSettings.cpp
|
||||
pluginsystem/IKonsolePlugin.cpp
|
||||
pluginsystem/PluginManager.cpp
|
||||
delegates/ProfileShortcutDelegate.cpp
|
||||
)
|
||||
|
||||
generate_export_header(konsoleapp BASE_NAME konsoleapp)
|
||||
|
||||
target_compile_definitions(konsoleapp PRIVATE -DRELEASE_SERVICE_VERSION="${RELEASE_SERVICE_VERSION}")
|
||||
|
||||
target_link_libraries(konsoleapp
|
||||
konsoleprivate
|
||||
KF6::XmlGui
|
||||
KF6::WindowSystem
|
||||
KF6::Bookmarks
|
||||
KF6::I18n
|
||||
KF6::KIOWidgets
|
||||
KF6::NotifyConfig
|
||||
KF6::Crash
|
||||
)
|
||||
|
||||
set_target_properties(konsoleapp PROPERTIES
|
||||
VERSION ${RELEASE_SERVICE_VERSION}
|
||||
)
|
||||
|
||||
install(TARGETS konsoleapp ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP)
|
||||
|
||||
set(konsole_SRCS
|
||||
main.cpp
|
||||
)
|
||||
|
||||
# Sets the icon on Windows and OSX
|
||||
ecm_add_app_icon(ICONS_SOURCES ICONS ${ICONS_SRCS})
|
||||
|
||||
add_executable(konsole ${konsole_SRCS} ${ICONS_SOURCES})
|
||||
target_link_libraries(konsole
|
||||
konsoleprivate
|
||||
konsoleapp
|
||||
KF6::XmlGui
|
||||
KF6::WindowSystem
|
||||
KF6::Bookmarks
|
||||
KF6::I18n
|
||||
KF6::KIOWidgets
|
||||
KF6::NotifyConfig
|
||||
KF6::Crash
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
set_target_properties(konsole PROPERTIES
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.konsole"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "Konsole"
|
||||
MACOSX_BUNDLE_DISPLAY_NAME "Konsole"
|
||||
MACOSX_BUNDLE_INFO_STRING "Konsole, the KDE terminal emulator"
|
||||
MACOSX_BUNDLE_LONG_VERSION_STRING "Konsole ${RELEASE_SERVICE_VERSION}"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${RELEASE_SERVICE_VERSION}"
|
||||
MACOSX_BUNDLE_COPYRIGHT "1997-2022 The Konsole Developers")
|
||||
endif()
|
||||
|
||||
install(TARGETS konsole ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
### Embedded Konsole KPart
|
||||
|
||||
set(konsolepart_PART_SRCS Part.cpp
|
||||
settings/PartInfo.cpp
|
||||
settings/ProfileSettings.cpp
|
||||
delegates/ProfileShortcutDelegate.cpp
|
||||
)
|
||||
|
||||
add_library(konsolepart MODULE ${konsolepart_PART_SRCS})
|
||||
generate_export_header(konsolepart BASE_NAME konsole)
|
||||
set_target_properties(konsolepart PROPERTIES DEFINE_SYMBOL KONSOLE_PART)
|
||||
target_link_libraries(konsolepart
|
||||
KF6::Parts
|
||||
KF6::XmlGui
|
||||
konsoleprivate
|
||||
)
|
||||
|
||||
install(TARGETS konsolepart DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf6/parts)
|
||||
|
||||
# Add cmake-options -DBUILD_COVERAGE=ON -DCOVERAGE_GENERATE_HTML=ON
|
||||
if(COVERAGE_GENERATE_HTML)
|
||||
# find required tools - no error checking
|
||||
find_program(LCOV lcov REQUIRED)
|
||||
find_program(GENHTML genhtml REQUIRED)
|
||||
|
||||
add_custom_target(coverage
|
||||
# runs tests to get .gcda files
|
||||
COMMAND ctest --progress
|
||||
# generate data
|
||||
COMMAND ${LCOV} --directory . --capture --output-file coverage.info
|
||||
# generate report
|
||||
COMMAND ${GENHTML} --ignore-errors source --demangle-cpp -o coverage coverage.info
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "CheckableSessionModel.h"
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
CheckableSessionModel::CheckableSessionModel(QObject *parent)
|
||||
: SessionListModel(parent)
|
||||
, _checkedSessions(QSet<Session *>())
|
||||
, _fixedSessions(QSet<Session *>())
|
||||
, _checkColumn(0)
|
||||
{
|
||||
}
|
||||
|
||||
void CheckableSessionModel::setCheckColumn(int column)
|
||||
{
|
||||
beginResetModel();
|
||||
_checkColumn = column;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
Qt::ItemFlags CheckableSessionModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
auto *session = static_cast<Session *>(index.internalPointer());
|
||||
|
||||
if (_fixedSessions.contains(session)) {
|
||||
return SessionListModel::flags(index) & ~Qt::ItemIsEnabled;
|
||||
}
|
||||
return SessionListModel::flags(index) | Qt::ItemIsUserCheckable;
|
||||
}
|
||||
|
||||
QVariant CheckableSessionModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (role == Qt::CheckStateRole && index.column() == _checkColumn) {
|
||||
auto *session = static_cast<Session *>(index.internalPointer());
|
||||
return QVariant::fromValue(static_cast<int>(_checkedSessions.contains(session) ? Qt::Checked : Qt::Unchecked));
|
||||
}
|
||||
return SessionListModel::data(index, role);
|
||||
}
|
||||
|
||||
bool CheckableSessionModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (role == Qt::CheckStateRole && index.column() == _checkColumn) {
|
||||
auto *session = static_cast<Session *>(index.internalPointer());
|
||||
|
||||
if (_fixedSessions.contains(session)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value.toInt() == Qt::Checked) {
|
||||
_checkedSessions.insert(session);
|
||||
} else {
|
||||
_checkedSessions.remove(session);
|
||||
}
|
||||
|
||||
Q_EMIT dataChanged(index, index);
|
||||
return true;
|
||||
}
|
||||
return SessionListModel::setData(index, value, role);
|
||||
}
|
||||
|
||||
void CheckableSessionModel::setCheckedSessions(const QSet<Session *> &sessions)
|
||||
{
|
||||
beginResetModel();
|
||||
_checkedSessions = sessions;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QSet<Session *> CheckableSessionModel::checkedSessions() const
|
||||
{
|
||||
return _checkedSessions;
|
||||
}
|
||||
|
||||
void CheckableSessionModel::setCheckable(Session *session, bool checkable)
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
if (!checkable) {
|
||||
_fixedSessions.insert(session);
|
||||
} else {
|
||||
_fixedSessions.remove(session);
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void CheckableSessionModel::sessionRemoved(Session *session)
|
||||
{
|
||||
_checkedSessions.remove(session);
|
||||
_fixedSessions.remove(session);
|
||||
}
|
||||
|
||||
#include "moc_CheckableSessionModel.cpp"
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef CHECKABLESESSIONMODEL_H
|
||||
#define CHECKABLESESSIONMODEL_H
|
||||
|
||||
// Qt
|
||||
#include <QSet>
|
||||
|
||||
// Konsole
|
||||
#include "session/Session.h"
|
||||
#include "session/SessionListModel.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
/**
|
||||
* A list of sessions with a checkbox next to each one which allows the
|
||||
* user to select a subset of the available sessions to perform
|
||||
* some action on them.
|
||||
*/
|
||||
class CheckableSessionModel : public SessionListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CheckableSessionModel(QObject *parent);
|
||||
|
||||
void setCheckColumn(int column);
|
||||
int checkColumn() const;
|
||||
|
||||
/**
|
||||
* Sets whether a session can be checked or un-checked.
|
||||
* Non-checkable items have the Qt::ItemIsEnabled flag unset.
|
||||
*/
|
||||
void setCheckable(Session *session, bool checkable);
|
||||
|
||||
/** Sets the list of sessions which are currently checked. */
|
||||
void setCheckedSessions(const QSet<Session *> &sessions);
|
||||
/** Returns the set of checked sessions. */
|
||||
QSet<Session *> checkedSessions() const;
|
||||
|
||||
// reimplemented from QAbstractItemModel
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
|
||||
protected:
|
||||
void sessionRemoved(Session *) override;
|
||||
|
||||
private:
|
||||
QSet<Session *> _checkedSessions;
|
||||
QSet<Session *> _fixedSessions;
|
||||
int _checkColumn;
|
||||
};
|
||||
|
||||
inline int CheckableSessionModel::checkColumn() const
|
||||
{
|
||||
return _checkColumn;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "CopyInputDialog.h"
|
||||
|
||||
// Qt
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
// Konsole
|
||||
#include "CheckableSessionModel.h"
|
||||
#include "ui_CopyInputDialog.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
CopyInputDialog::CopyInputDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, _ui(nullptr)
|
||||
, _model(nullptr)
|
||||
, _masterSession(nullptr)
|
||||
{
|
||||
setWindowTitle(i18n("Copy Input"));
|
||||
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
auto mainWidget = new QWidget(this);
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
setLayout(mainLayout);
|
||||
mainLayout->addWidget(mainWidget);
|
||||
QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
|
||||
okButton->setDefault(true);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &CopyInputDialog::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &CopyInputDialog::reject);
|
||||
mainLayout->addWidget(buttonBox);
|
||||
|
||||
setWindowModality(Qt::WindowModal);
|
||||
|
||||
_ui = new Ui::CopyInputDialog();
|
||||
_ui->setupUi(mainWidget);
|
||||
|
||||
connect(_ui->selectAllButton, &QPushButton::clicked, this, &Konsole::CopyInputDialog::selectAll);
|
||||
connect(_ui->deselectAllButton, &QPushButton::clicked, this, &Konsole::CopyInputDialog::deselectAll);
|
||||
|
||||
_ui->filterEdit->setClearButtonEnabled(true);
|
||||
_ui->filterEdit->setFocus();
|
||||
|
||||
_model = new CheckableSessionModel(parent);
|
||||
_model->setCheckColumn(1);
|
||||
_model->setSessions(SessionManager::instance()->sessions());
|
||||
|
||||
auto filterProxyModel = new QSortFilterProxyModel(this);
|
||||
filterProxyModel->setDynamicSortFilter(true);
|
||||
filterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
filterProxyModel->setSourceModel(_model);
|
||||
filterProxyModel->setFilterKeyColumn(-1);
|
||||
|
||||
connect(_ui->filterEdit, &QLineEdit::textChanged, filterProxyModel, &QSortFilterProxyModel::setFilterFixedString);
|
||||
|
||||
_ui->sessionList->setModel(filterProxyModel);
|
||||
_ui->sessionList->setColumnHidden(0, true); // Hide number column
|
||||
_ui->sessionList->header()->hide();
|
||||
}
|
||||
|
||||
CopyInputDialog::~CopyInputDialog()
|
||||
{
|
||||
delete _ui;
|
||||
}
|
||||
|
||||
void CopyInputDialog::setChosenSessions(const QSet<Session *> &sessions)
|
||||
{
|
||||
QSet<Session *> checked = sessions;
|
||||
if (!_masterSession.isNull()) {
|
||||
checked.insert(_masterSession);
|
||||
}
|
||||
|
||||
_model->setCheckedSessions(checked);
|
||||
}
|
||||
|
||||
QSet<Session *> CopyInputDialog::chosenSessions() const
|
||||
{
|
||||
return _model->checkedSessions();
|
||||
}
|
||||
|
||||
void CopyInputDialog::setMasterSession(Session *session)
|
||||
{
|
||||
if (!_masterSession.isNull()) {
|
||||
_model->setCheckable(_masterSession, true);
|
||||
}
|
||||
|
||||
_model->setCheckable(session, false);
|
||||
QSet<Session *> checked = _model->checkedSessions();
|
||||
checked.insert(session);
|
||||
_model->setCheckedSessions(checked);
|
||||
|
||||
_masterSession = session;
|
||||
}
|
||||
|
||||
void CopyInputDialog::setSelectionChecked(bool checked)
|
||||
{
|
||||
QAbstractItemModel *model = _ui->sessionList->model();
|
||||
int rows = model->rowCount();
|
||||
|
||||
const QModelIndexList selected = _ui->sessionList->selectionModel()->selectedIndexes();
|
||||
|
||||
if (selected.count() > 1) {
|
||||
for (const QModelIndex &index : selected) {
|
||||
setRowChecked(index.row(), checked);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < rows; i++) {
|
||||
setRowChecked(i, checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CopyInputDialog::setRowChecked(int row, bool checked)
|
||||
{
|
||||
QAbstractItemModel *model = _ui->sessionList->model();
|
||||
QModelIndex index = model->index(row, _model->checkColumn());
|
||||
model->setData(index, static_cast<int>(checked ? Qt::Checked : Qt::Unchecked), Qt::CheckStateRole);
|
||||
}
|
||||
|
||||
#include "moc_CopyInputDialog.cpp"
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef COPYINPUTDIALOG_H
|
||||
#define COPYINPUTDIALOG_H
|
||||
|
||||
// Qt
|
||||
#include <QPointer>
|
||||
#include <QSet>
|
||||
|
||||
// KDE
|
||||
#include <QDialog>
|
||||
|
||||
// Konsole
|
||||
#include "konsoleprivate_export.h"
|
||||
#include "session/Session.h"
|
||||
#include "session/SessionListModel.h"
|
||||
#include "session/SessionManager.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class CopyInputDialog;
|
||||
}
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class CheckableSessionModel;
|
||||
|
||||
/**
|
||||
* Dialog which allows the user to mark a list of sessions to copy
|
||||
* the input from the current session to. The current session is
|
||||
* set using setMasterSession(). After the dialog has been executed,
|
||||
* the set of chosen sessions can be retrieved using chosenSessions()
|
||||
*/
|
||||
class KONSOLEPRIVATE_EXPORT CopyInputDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CopyInputDialog(QWidget *parent = nullptr);
|
||||
~CopyInputDialog() override;
|
||||
/**
|
||||
* Sets the 'source' session whose input will be copied to
|
||||
* other sessions. This session is displayed grayed out in the list
|
||||
* and cannot be unchecked.
|
||||
*/
|
||||
void setMasterSession(Session *session);
|
||||
|
||||
/** Sets the sessions in the list which are checked. */
|
||||
void setChosenSessions(const QSet<Session *> &sessions);
|
||||
/** Set setChosenSessions() */
|
||||
QSet<Session *> chosenSessions() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
void selectAll()
|
||||
{
|
||||
setSelectionChecked(true);
|
||||
}
|
||||
|
||||
void deselectAll()
|
||||
{
|
||||
setSelectionChecked(false);
|
||||
}
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(CopyInputDialog)
|
||||
|
||||
// Checks or unchecks selected sessions. If there are no
|
||||
// selected items then all sessions are checked or unchecked
|
||||
void setSelectionChecked(bool checked);
|
||||
void setRowChecked(int row, bool checked);
|
||||
|
||||
Ui::CopyInputDialog *_ui;
|
||||
CheckableSessionModel *_model;
|
||||
QPointer<Session> _masterSession;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // COPYINPUTDIALOG_H
|
||||
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CopyInputDialog</class>
|
||||
<widget class="QWidget" name="CopyInputDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>363</width>
|
||||
<height>223</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Filter:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="filterEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeView" name="sessionList">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SelectionMode::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="selectAllButton">
|
||||
<property name="text">
|
||||
<string>Select All</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="deselectAllButton">
|
||||
<property name="text">
|
||||
<string>Deselect All</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
|
||||
SPDX-FileCopyrightText: 1996 Matthias Ettrich <ettrich@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "Emulation.h"
|
||||
|
||||
// Qt
|
||||
#include <QKeyEvent>
|
||||
|
||||
// Konsole
|
||||
#include "Screen.h"
|
||||
#include "ScreenWindow.h"
|
||||
#include "keyboardtranslator/KeyboardTranslator.h"
|
||||
#include "keyboardtranslator/KeyboardTranslatorManager.h"
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
Emulation::Emulation()
|
||||
{
|
||||
// create screens with a default size
|
||||
_screen[0] = new Screen(40, 80);
|
||||
_screen[1] = new Screen(40, 80);
|
||||
_currentScreen = _screen[0];
|
||||
|
||||
QObject::connect(&_bulkTimer1, &QTimer::timeout, this, &Konsole::Emulation::showBulk);
|
||||
QObject::connect(&_bulkTimer2, &QTimer::timeout, this, &Konsole::Emulation::showBulk);
|
||||
|
||||
// listen for mouse status changes
|
||||
connect(this, &Konsole::Emulation::programRequestsMouseTracking, this, &Konsole::Emulation::setUsesMouseTracking);
|
||||
connect(this, &Konsole::Emulation::programBracketedPasteModeChanged, this, &Konsole::Emulation::bracketedPasteModeChanged);
|
||||
}
|
||||
|
||||
bool Emulation::programUsesMouseTracking() const
|
||||
{
|
||||
return _usesMouseTracking;
|
||||
}
|
||||
|
||||
void Emulation::setUsesMouseTracking(bool usesMouseTracking)
|
||||
{
|
||||
_usesMouseTracking = usesMouseTracking;
|
||||
}
|
||||
|
||||
bool Emulation::programBracketedPasteMode() const
|
||||
{
|
||||
return _bracketedPasteMode;
|
||||
}
|
||||
|
||||
void Emulation::bracketedPasteModeChanged(bool bracketedPasteMode)
|
||||
{
|
||||
_bracketedPasteMode = bracketedPasteMode;
|
||||
}
|
||||
|
||||
ScreenWindow *Emulation::createWindow()
|
||||
{
|
||||
auto window = new ScreenWindow(_currentScreen);
|
||||
_windows << window;
|
||||
|
||||
connect(window, &Konsole::ScreenWindow::selectionChanged, this, &Konsole::Emulation::bufferedUpdate);
|
||||
connect(window, &Konsole::ScreenWindow::selectionChanged, this, &Konsole::Emulation::checkSelectedText);
|
||||
|
||||
connect(this, &Konsole::Emulation::outputChanged, window, &Konsole::ScreenWindow::notifyOutputChanged);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
void Emulation::setCurrentTerminalDisplay(TerminalDisplay *display)
|
||||
{
|
||||
_screen[0]->setCurrentTerminalDisplay(display);
|
||||
_screen[1]->setCurrentTerminalDisplay(display);
|
||||
}
|
||||
|
||||
void Emulation::checkScreenInUse()
|
||||
{
|
||||
Q_EMIT primaryScreenInUse(_currentScreen == _screen[0]);
|
||||
}
|
||||
|
||||
void Emulation::checkSelectedText()
|
||||
{
|
||||
bool isEmpty = !_currentScreen->hasSelection();
|
||||
Q_EMIT selectionChanged(isEmpty);
|
||||
}
|
||||
|
||||
Emulation::~Emulation()
|
||||
{
|
||||
for (ScreenWindow *window : std::as_const(_windows)) {
|
||||
delete window;
|
||||
}
|
||||
|
||||
delete _screen[0];
|
||||
delete _screen[1];
|
||||
}
|
||||
|
||||
void Emulation::setPeekPrimary(const bool doPeek)
|
||||
{
|
||||
if (doPeek == _peekingPrimary) {
|
||||
return;
|
||||
}
|
||||
_peekingPrimary = doPeek;
|
||||
setScreenInternal(doPeek ? 0 : _activeScreenIndex);
|
||||
Q_EMIT outputChanged();
|
||||
}
|
||||
|
||||
void Emulation::setScreen(int index)
|
||||
{
|
||||
_activeScreenIndex = index;
|
||||
_peekingPrimary = false;
|
||||
setScreenInternal(_activeScreenIndex);
|
||||
}
|
||||
|
||||
void Emulation::setScreenInternal(int index)
|
||||
{
|
||||
Screen *oldScreen = _currentScreen;
|
||||
_currentScreen = _screen[index & 1];
|
||||
if (_currentScreen != oldScreen) {
|
||||
// tell all windows onto this emulation to switch to the newly active screen
|
||||
for (ScreenWindow *window : std::as_const(_windows)) {
|
||||
window->setScreen(_currentScreen);
|
||||
}
|
||||
|
||||
checkScreenInUse();
|
||||
checkSelectedText();
|
||||
}
|
||||
}
|
||||
|
||||
void Emulation::clearHistory()
|
||||
{
|
||||
_screen[0]->setScroll(_screen[0]->getScroll(), false);
|
||||
}
|
||||
|
||||
void Emulation::setHistory(const HistoryType &history)
|
||||
{
|
||||
_screen[0]->setScroll(history);
|
||||
|
||||
showBulk();
|
||||
}
|
||||
|
||||
const HistoryType &Emulation::history() const
|
||||
{
|
||||
return _screen[0]->getScroll();
|
||||
}
|
||||
|
||||
void Emulation::setCodec(const QTextCodec *codec)
|
||||
{
|
||||
if (codec != nullptr) {
|
||||
_codec = codec;
|
||||
|
||||
_decoder.reset(_codec->makeDecoder());
|
||||
|
||||
Q_EMIT useUtf8Request(utf8());
|
||||
} else {
|
||||
#if defined(Q_OS_WIN)
|
||||
setCodec(Utf8Codec);
|
||||
#else
|
||||
setCodec(LocaleCodec);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Emulation::setCodec(EmulationCodec codec)
|
||||
{
|
||||
if (codec == Utf8Codec) {
|
||||
setCodec(QTextCodec::codecForName("utf8"));
|
||||
} else if (codec == LocaleCodec) {
|
||||
setCodec(QTextCodec::codecForLocale());
|
||||
}
|
||||
}
|
||||
|
||||
void Emulation::setKeyBindings(const QString &name)
|
||||
{
|
||||
_keyTranslator = KeyboardTranslatorManager::instance()->findTranslator(name);
|
||||
if (_keyTranslator == nullptr) {
|
||||
_keyTranslator = KeyboardTranslatorManager::instance()->defaultTranslator();
|
||||
}
|
||||
}
|
||||
|
||||
QString Emulation::keyBindings() const
|
||||
{
|
||||
return _keyTranslator->name();
|
||||
}
|
||||
|
||||
// process application unicode input to terminal
|
||||
// this is a trivial scanner
|
||||
void Emulation::receiveChars(const QVector<uint> &chars)
|
||||
{
|
||||
for (uint c : chars) {
|
||||
c &= 0xff;
|
||||
switch (c) {
|
||||
case '\b':
|
||||
_currentScreen->backspace();
|
||||
break;
|
||||
case '\t':
|
||||
_currentScreen->tab();
|
||||
break;
|
||||
case '\n':
|
||||
_currentScreen->newLine();
|
||||
break;
|
||||
case '\r':
|
||||
_currentScreen->toStartOfLine();
|
||||
break;
|
||||
case 0x07:
|
||||
Q_EMIT bell();
|
||||
break;
|
||||
default:
|
||||
_currentScreen->displayCharacter(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Emulation::sendKeyEvent(QKeyEvent *ev)
|
||||
{
|
||||
if (!ev->text().isEmpty()) {
|
||||
// A block of text
|
||||
// Note that the text is proper unicode.
|
||||
// We should do a conversion here
|
||||
Q_EMIT sendData(ev->text().toLocal8Bit());
|
||||
}
|
||||
}
|
||||
|
||||
void Emulation::receiveData(const char *text, int length)
|
||||
{
|
||||
Q_ASSERT(_decoder);
|
||||
|
||||
bufferedUpdate();
|
||||
|
||||
// send characters to terminal emulator
|
||||
const QVector<uint> chars = _decoder->toUnicode(text, length).toUcs4();
|
||||
receiveChars(chars);
|
||||
|
||||
// look for z-modem indicator
|
||||
//-- someone who understands more about z-modems that I do may be able to move
|
||||
// this check into the above for loop?
|
||||
auto *found = static_cast<const char *>(memchr(text, '\030', length));
|
||||
if (found) {
|
||||
auto startPos = found - text;
|
||||
if (startPos < 0) {
|
||||
return;
|
||||
}
|
||||
for (int i = startPos; i < length - 4; i++) {
|
||||
if (text[i] == '\030') {
|
||||
if (qstrncmp(text + i + 1, "B00", 3) == 0) {
|
||||
Q_EMIT zmodemDownloadDetected();
|
||||
} else if (qstrncmp(text + i + 1, "B01", 3) == 0) {
|
||||
Q_EMIT zmodemUploadDetected();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Emulation::writeToStream(TerminalCharacterDecoder *decoder, int startLine, int endLine)
|
||||
{
|
||||
_currentScreen->writeLinesToStream(decoder, startLine, endLine);
|
||||
}
|
||||
|
||||
int Emulation::lineCount() const
|
||||
{
|
||||
// sum number of lines currently on _screen plus number of lines in history
|
||||
return _currentScreen->getLines() + _currentScreen->getHistLines();
|
||||
}
|
||||
|
||||
void Emulation::showBulk()
|
||||
{
|
||||
_bulkTimer1.stop();
|
||||
_bulkTimer2.stop();
|
||||
|
||||
Q_EMIT outputChanged();
|
||||
|
||||
_currentScreen->resetScrolledLines();
|
||||
_currentScreen->resetDroppedLines();
|
||||
}
|
||||
|
||||
void Emulation::bufferedUpdate()
|
||||
{
|
||||
static const int BULK_TIMEOUT1 = 10;
|
||||
static const int BULK_TIMEOUT2 = 40;
|
||||
|
||||
_bulkTimer1.setSingleShot(true);
|
||||
_bulkTimer1.start(BULK_TIMEOUT1);
|
||||
if (!_bulkTimer2.isActive()) {
|
||||
_bulkTimer2.setSingleShot(true);
|
||||
_bulkTimer2.start(BULK_TIMEOUT2);
|
||||
}
|
||||
}
|
||||
|
||||
char Emulation::eraseChar() const
|
||||
{
|
||||
return '\b';
|
||||
}
|
||||
|
||||
void Emulation::setImageSize(int lines, int columns)
|
||||
{
|
||||
if ((lines < 1) || (columns < 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QSize screenSize[2] = {QSize(_screen[0]->getColumns(), _screen[0]->getLines()), //
|
||||
QSize(_screen[1]->getColumns(), _screen[1]->getLines())};
|
||||
QSize newSize(columns, lines);
|
||||
|
||||
if (newSize == screenSize[0] && newSize == screenSize[1]) {
|
||||
// If this method is called for the first time, always emit
|
||||
// imageSizeChanged() signal, even if the new size is the same as the
|
||||
// current size. See #176902
|
||||
if (!_imageSizeInitialized) {
|
||||
Q_EMIT imageSizeChanged(lines, columns);
|
||||
}
|
||||
} else {
|
||||
_screen[0]->resizeImage(lines, columns);
|
||||
_screen[1]->resizeImage(lines, columns);
|
||||
|
||||
Q_EMIT imageSizeChanged(lines, columns);
|
||||
|
||||
bufferedUpdate();
|
||||
}
|
||||
|
||||
if (!_imageSizeInitialized) {
|
||||
_imageSizeInitialized = true;
|
||||
|
||||
Q_EMIT imageSizeInitialized();
|
||||
}
|
||||
}
|
||||
|
||||
QSize Emulation::imageSize() const
|
||||
{
|
||||
return {_currentScreen->getColumns(), _currentScreen->getLines()};
|
||||
}
|
||||
|
||||
#include "moc_Emulation.cpp"
|
||||
@@ -0,0 +1,489 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef EMULATION_H
|
||||
#define EMULATION_H
|
||||
|
||||
// Qt
|
||||
#include <QSize>
|
||||
#include <QTextCodec>
|
||||
#include <QTimer>
|
||||
|
||||
// Konsole
|
||||
#include "Enumeration.h"
|
||||
#include "konsoleprivate_export.h"
|
||||
#include "terminalDisplay/TerminalDisplay.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QKeyEvent;
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class KeyboardTranslator;
|
||||
class HistoryType;
|
||||
class Screen;
|
||||
class ScreenWindow;
|
||||
class TerminalCharacterDecoder;
|
||||
|
||||
/**
|
||||
* Base class for terminal emulation back-ends.
|
||||
*
|
||||
* The back-end is responsible for decoding an incoming character stream and
|
||||
* producing an output image of characters.
|
||||
*
|
||||
* When input from the terminal is received, the receiveData() slot should be called with
|
||||
* the data which has arrived. The emulation will process the data and update the
|
||||
* screen image accordingly. The codec used to decode the incoming character stream
|
||||
* into the unicode characters used internally can be specified using setCodec()
|
||||
*
|
||||
* The size of the screen image can be specified by calling setImageSize() with the
|
||||
* desired number of lines and columns. When new lines are added, old content
|
||||
* is moved into a history store, which can be set by calling setHistory().
|
||||
*
|
||||
* The screen image can be accessed by creating a ScreenWindow onto this emulation
|
||||
* by calling createWindow(). Screen windows provide access to a section of the
|
||||
* output. Each screen window covers the same number of lines and columns as the
|
||||
* image size returned by imageSize(). The screen window can be moved up and down
|
||||
* and provides transparent access to both the current on-screen image and the
|
||||
* previous output. The screen windows emit an outputChanged signal
|
||||
* when the section of the image they are looking at changes.
|
||||
* Graphical views can then render the contents of a screen window, listening for notifications
|
||||
* of output changes from the screen window which they are associated with and updating
|
||||
* accordingly.
|
||||
*
|
||||
* The emulation also is also responsible for converting input from the connected views such
|
||||
* as keypresses and mouse activity into a character string which can be sent
|
||||
* to the terminal program. Key presses can be processed by calling the sendKeyEvent() slot,
|
||||
* while mouse events can be processed using the sendMouseEvent() slot. When the character
|
||||
* stream has been produced, the emulation will emit a sendData() signal with a pointer
|
||||
* to the character buffer. This data should be fed to the standard input of the terminal
|
||||
* process. The translation of key presses into an output character stream is performed
|
||||
* using a lookup in a set of key bindings which map key sequences to output
|
||||
* character sequences. The name of the key bindings set used can be specified using
|
||||
* setKeyBindings()
|
||||
*
|
||||
* The emulation maintains certain state information which changes depending on the
|
||||
* input received. The emulation can be reset back to its starting state by calling
|
||||
* reset().
|
||||
*
|
||||
* The emulation also maintains an activity state, which specifies whether
|
||||
* terminal is currently active ( when data is received ), normal
|
||||
* ( when the terminal is idle or receiving user input ) or trying
|
||||
* to alert the user ( also known as a "Bell" event ). The stateSet() signal
|
||||
* is emitted whenever the activity state is set. This can be used to determine
|
||||
* how long the emulation has been active/idle for and also respond to
|
||||
* a 'bell' event in different ways.
|
||||
*/
|
||||
class KONSOLEPRIVATE_EXPORT Emulation : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/** Constructs a new terminal emulation */
|
||||
Emulation();
|
||||
~Emulation() override;
|
||||
|
||||
/**
|
||||
* Creates a new window onto the output from this emulation. The contents
|
||||
* of the window are then rendered by views which are set to use this window using the
|
||||
* TerminalDisplay::setScreenWindow() method.
|
||||
*/
|
||||
ScreenWindow *createWindow();
|
||||
|
||||
/**
|
||||
* Associates a TerminalDisplay with this emulation.
|
||||
*/
|
||||
void setCurrentTerminalDisplay(TerminalDisplay *display);
|
||||
|
||||
/** Returns the size of the screen image which the emulation produces */
|
||||
QSize imageSize() const;
|
||||
|
||||
/**
|
||||
* Returns the total number of lines, including those stored in the history.
|
||||
*/
|
||||
int lineCount() const;
|
||||
|
||||
/**
|
||||
* Sets the history store used by this emulation. When new lines
|
||||
* are added to the output, older lines at the top of the screen are transferred to a history
|
||||
* store.
|
||||
*
|
||||
* The number of lines which are kept and the storage location depend on the
|
||||
* type of store.
|
||||
*/
|
||||
void setHistory(const HistoryType &);
|
||||
/** Returns the history store used by this emulation. See setHistory() */
|
||||
const HistoryType &history() const;
|
||||
/** Clears the history scroll. */
|
||||
virtual void clearHistory();
|
||||
|
||||
/**
|
||||
* Copies the output history from @p startLine to @p endLine
|
||||
* into @p stream, using @p decoder to convert the terminal
|
||||
* characters into text.
|
||||
*
|
||||
* @param decoder A decoder which converts lines of terminal characters with
|
||||
* appearance attributes into output text. PlainTextDecoder is the most commonly
|
||||
* used decoder.
|
||||
* @param startLine Index of first line to copy
|
||||
* @param endLine Index of last line to copy
|
||||
*/
|
||||
virtual void writeToStream(TerminalCharacterDecoder *decoder, int startLine, int endLine);
|
||||
|
||||
/** Returns the codec used to decode incoming characters. See setCodec() */
|
||||
const QTextCodec *codec() const
|
||||
{
|
||||
return _codec;
|
||||
}
|
||||
|
||||
/** Sets the codec used to decode incoming characters. */
|
||||
void setCodec(const QTextCodec *);
|
||||
|
||||
/**
|
||||
* Convenience method.
|
||||
* Returns true if the current codec used to decode incoming
|
||||
* characters is UTF-8
|
||||
*/
|
||||
bool utf8() const
|
||||
{
|
||||
Q_ASSERT(_codec);
|
||||
return _codec->mibEnum() == 106;
|
||||
}
|
||||
|
||||
/** Returns the special character used for erasing character. */
|
||||
virtual char eraseChar() const;
|
||||
|
||||
/**
|
||||
* Sets the key bindings used to key events
|
||||
* ( received through sendKeyEvent() ) into character
|
||||
* streams to send to the terminal.
|
||||
*/
|
||||
void setKeyBindings(const QString &name);
|
||||
/**
|
||||
* Returns the name of the emulation's current key bindings.
|
||||
* See setKeyBindings()
|
||||
*/
|
||||
QString keyBindings() const;
|
||||
|
||||
/**
|
||||
* Copies the current image into the history and clears the screen.
|
||||
*/
|
||||
virtual void clearEntireScreen() = 0;
|
||||
|
||||
/** Resets the state of the terminal.
|
||||
*
|
||||
* @param softReset The reset was initiated by DECSTR
|
||||
* @param preservePrompt Try to preserve the command prompt */
|
||||
virtual void reset(bool softReset = false, bool preservePrompt = false) = 0;
|
||||
|
||||
/**
|
||||
* Returns true if the active terminal program is interested in Mouse
|
||||
* Tracking events.
|
||||
*
|
||||
* The programRequestsMouseTracking() signal is emitted when a program
|
||||
* indicates it's interested in Mouse Tracking events.
|
||||
*
|
||||
* See MODE_Mouse100{0,1,2,3} in Vt102Emulation.
|
||||
*/
|
||||
bool programUsesMouseTracking() const;
|
||||
|
||||
bool programBracketedPasteMode() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
/** Change the size of the emulation's image */
|
||||
virtual void setImageSize(int lines, int columns);
|
||||
|
||||
/**
|
||||
* Interprets a sequence of characters and sends the result to the terminal.
|
||||
* This is equivalent to calling sendKeyEvent() for each character in @p text in succession.
|
||||
*/
|
||||
virtual void sendText(const QString &text) = 0;
|
||||
|
||||
/**
|
||||
* Interprets a key press event and emits the sendData() signal with
|
||||
* the resulting character stream.
|
||||
*/
|
||||
virtual void sendKeyEvent(QKeyEvent *);
|
||||
|
||||
/**
|
||||
* Converts information about a mouse event into an xterm-compatible escape
|
||||
* sequence and emits the character sequence via sendData()
|
||||
*/
|
||||
virtual void sendMouseEvent(int buttons, int column, int line, int eventType) = 0;
|
||||
|
||||
/**
|
||||
* Sends a string of characters to the foreground terminal process.
|
||||
*
|
||||
* @param string The characters to send.
|
||||
*/
|
||||
virtual void sendString(const QByteArray &string) = 0;
|
||||
|
||||
/**
|
||||
* Processes an incoming stream of characters. receiveData() decodes the incoming
|
||||
* character buffer using the current codec(), and then calls receiveChar() for
|
||||
* each unicode character in the resulting buffer.
|
||||
*
|
||||
* receiveData() also starts a timer which causes the outputChanged() signal
|
||||
* to be emitted when it expires. The timer allows multiple updates in quick
|
||||
* succession to be buffered into a single outputChanged() signal emission.
|
||||
*
|
||||
* @param text A string of characters received from the terminal program.
|
||||
* @param length The length of @p text
|
||||
*/
|
||||
void receiveData(const char *text, int length);
|
||||
|
||||
/**
|
||||
* Sends information about the focus event to the terminal.
|
||||
*/
|
||||
virtual void focusChanged(bool focused) = 0;
|
||||
|
||||
void setPeekPrimary(const bool doPeek);
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
/**
|
||||
* Emitted when a buffer of data is ready to send to the
|
||||
* standard input of the terminal.
|
||||
*
|
||||
* @param data The buffer of data ready to be sent
|
||||
*/
|
||||
void sendData(const QByteArray &data);
|
||||
|
||||
/**
|
||||
* Requests that the pty used by the terminal process
|
||||
* be set to UTF 8 mode.
|
||||
*
|
||||
* Refer to the IUTF8 entry in termios(3) for more information.
|
||||
*/
|
||||
void useUtf8Request(bool);
|
||||
|
||||
/**
|
||||
* Emitted when bell appeared
|
||||
*/
|
||||
void bell();
|
||||
|
||||
/**
|
||||
* Emitted when the special sequence indicating the request for data
|
||||
* transmission through ZModem protocol is detected.
|
||||
*/
|
||||
void zmodemDownloadDetected();
|
||||
void zmodemUploadDetected();
|
||||
|
||||
/**
|
||||
* This is emitted when the program (typically editors and other full-screen
|
||||
* applications, ones that take up the whole terminal display), running in
|
||||
* the terminal indicates whether or not it is interested in Mouse Tracking
|
||||
* events. This is an XTerm extension, for more information have a look at:
|
||||
* https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
*
|
||||
* @param usesMouseTracking This will be true if the program is interested
|
||||
* in Mouse Tracking events or false otherwise.
|
||||
*/
|
||||
void programRequestsMouseTracking(bool usesMouseTracking);
|
||||
|
||||
void enableAlternateScrolling(bool enable);
|
||||
|
||||
void programBracketedPasteModeChanged(bool bracketedPasteMode);
|
||||
|
||||
/**
|
||||
* Emitted when the contents of the screen image change.
|
||||
* The emulation buffers the updates from successive image changes,
|
||||
* and only emits outputChanged() at sensible intervals when
|
||||
* there is a lot of terminal activity.
|
||||
*
|
||||
* Normally there is no need for objects other than the screen windows
|
||||
* created with createWindow() to listen for this signal.
|
||||
*
|
||||
* ScreenWindow objects created using createWindow() will emit their
|
||||
* own outputChanged() signal in response to this signal.
|
||||
*/
|
||||
void outputChanged();
|
||||
|
||||
/**
|
||||
* Emitted when the program running in the terminal wishes to update
|
||||
* certain session attributes. This allows terminal programs to customize
|
||||
* certain aspects of the terminal emulation display.
|
||||
*
|
||||
* This signal is emitted when the escape sequence "\033]ARG;VALUE\007"
|
||||
* is received in an input string, where ARG is a number specifying
|
||||
* what should be changed and VALUE is a string specifying the new value.
|
||||
*
|
||||
* @param attribute Specifies which session attribute to change:
|
||||
* <ul>
|
||||
* <li> 0 - Set window icon text and session title to @p newValue</li>
|
||||
* <li> 1 - Set window icon text to @p newValue</li>
|
||||
* <li> 2 - Set session title to @p newValue</li>
|
||||
* <li> 11 - Set the session's default background color to @p newValue,
|
||||
* where @p newValue can be an HTML-style string ("#RRGGBB") or a
|
||||
* named color (e.g. 'red', 'blue'). For more details see:
|
||||
* https://doc.qt.io/qt-5/qcolor.html#setNamedColor
|
||||
* </li>
|
||||
* <li> 31 - Supposedly treats @p newValue as a URL and opens it (NOT
|
||||
* IMPLEMENTED)
|
||||
* </li>
|
||||
* <li> 32 - Sets the icon associated with the session. @p newValue
|
||||
* is the name of the icon to use, which can be the name of any
|
||||
* icon in the current KDE icon theme (eg: 'konsole', 'kate',
|
||||
* 'folder_home')
|
||||
* </li>
|
||||
* </ul>
|
||||
* @param newValue Specifies the new value of the session attribute
|
||||
*/
|
||||
void sessionAttributeChanged(int attribute, const QString &newValue);
|
||||
|
||||
/**
|
||||
* Emitted when the terminal emulator's size has changed
|
||||
*/
|
||||
void imageSizeChanged(int lineCount, int columnCount);
|
||||
|
||||
/**
|
||||
* Emitted when the setImageSize() is called on this emulation for
|
||||
* the first time.
|
||||
*/
|
||||
void imageSizeInitialized();
|
||||
|
||||
/**
|
||||
* Emitted after receiving the escape sequence which asks to change
|
||||
* the terminal emulator's size
|
||||
*/
|
||||
void imageResizeRequest(const QSize &sizz);
|
||||
|
||||
/**
|
||||
* Emitted when the terminal program requests to change various properties
|
||||
* of the terminal display.
|
||||
*
|
||||
* A profile change command occurs when a special escape sequence, followed
|
||||
* by a string containing a series of name and value pairs is received.
|
||||
* This string can be parsed using a ProfileCommandParser instance.
|
||||
*
|
||||
* @param text A string expected to contain a series of key and value pairs in
|
||||
* the form: name=value;name2=value2 ...
|
||||
*/
|
||||
void profileChangeCommandReceived(const QString &text);
|
||||
|
||||
/**
|
||||
* Emitted when a flow control key combination ( Ctrl+S or Ctrl+Q ) is pressed.
|
||||
* @param suspendKeyPressed True if Ctrl+S was pressed to suspend output or Ctrl+Q to
|
||||
* resume output.
|
||||
*/
|
||||
void flowControlKeyPressed(bool suspendKeyPressed);
|
||||
|
||||
/**
|
||||
* Emitted when the active screen is switched, to indicate whether the primary
|
||||
* screen is in use.
|
||||
*/
|
||||
void primaryScreenInUse(bool use);
|
||||
|
||||
/**
|
||||
* Emitted when the text selection is changed
|
||||
*/
|
||||
void selectionChanged(const bool selectionEmpty);
|
||||
|
||||
/**
|
||||
* Emitted when terminal code requiring terminal's response received.
|
||||
*/
|
||||
void sessionAttributeRequest(int id, uint terminator);
|
||||
|
||||
/**
|
||||
* Emitted when Set Cursor Style (DECSCUSR) escape sequences are sent
|
||||
* to the terminal.
|
||||
* @p shape cursor shape
|
||||
* @p isBlinking if true, the cursor will be set to blink
|
||||
* @p customColor custom cursor color
|
||||
*/
|
||||
void setCursorStyleRequest(Enum::CursorShapeEnum shape = Enum::BlockCursor, bool isBlinking = false, const QColor &customColor = {});
|
||||
|
||||
/**
|
||||
* Emitted when reset() is called to reset the cursor style to the
|
||||
* current profile cursor shape and blinking settings.
|
||||
*/
|
||||
void resetCursorStyleRequest();
|
||||
|
||||
void toggleUrlExtractionRequest();
|
||||
|
||||
protected:
|
||||
virtual void setMode(int mode) = 0;
|
||||
virtual void resetMode(int mode) = 0;
|
||||
|
||||
/**
|
||||
* Processes an incoming character. See receiveData()
|
||||
* @p c A unicode character code.
|
||||
*/
|
||||
virtual void receiveChars(const QVector<uint> &c);
|
||||
|
||||
/**
|
||||
* Sets the active screen. The terminal has two screens, primary and alternate.
|
||||
* The primary screen is used by default. When certain interactive programs such
|
||||
* as Vim are run, they trigger a switch to the alternate screen.
|
||||
*
|
||||
* @param index 0 to switch to the primary screen, or 1 to switch to the alternate screen
|
||||
*/
|
||||
void setScreen(int index);
|
||||
|
||||
enum EmulationCodec {
|
||||
LocaleCodec = 0,
|
||||
Utf8Codec = 1,
|
||||
};
|
||||
|
||||
void setCodec(EmulationCodec codec);
|
||||
|
||||
QList<ScreenWindow *> _windows;
|
||||
|
||||
Screen *_currentScreen = nullptr; // pointer to the screen which is currently active,
|
||||
// this is one of the elements in the screen[] array
|
||||
|
||||
Screen *_screen[2]; // 0 = primary screen ( used by most programs, including the shell
|
||||
// scrollbars are enabled in this mode )
|
||||
// 1 = alternate ( used by vi , emacs etc.
|
||||
// scrollbars are not enabled in this mode )
|
||||
|
||||
// decodes an incoming C-style character stream into a unicode QString using
|
||||
// the current text codec. (this allows for rendering of non-ASCII characters in text files etc.)
|
||||
const QTextCodec *_codec = nullptr;
|
||||
std::unique_ptr<QTextDecoder> _decoder;
|
||||
const KeyboardTranslator *_keyTranslator = nullptr; // the keyboard layout
|
||||
|
||||
protected Q_SLOTS:
|
||||
/**
|
||||
* Schedules an update of attached views.
|
||||
* Repeated calls to bufferedUpdate() in close succession will result in only a single update,
|
||||
* much like the Qt buffered update of widgets.
|
||||
*/
|
||||
void bufferedUpdate();
|
||||
|
||||
// used to emit the primaryScreenInUse(bool) signal
|
||||
void checkScreenInUse();
|
||||
|
||||
// used to emit the selectionChanged(QString) signal
|
||||
void checkSelectedText();
|
||||
|
||||
private Q_SLOTS:
|
||||
// triggered by timer, causes the emulation to send an updated screen image to each
|
||||
// view
|
||||
void showBulk();
|
||||
|
||||
void setUsesMouseTracking(bool usesMouseTracking);
|
||||
|
||||
void bracketedPasteModeChanged(bool bracketedPasteMode);
|
||||
|
||||
private:
|
||||
void setScreenInternal(int index);
|
||||
Q_DISABLE_COPY(Emulation)
|
||||
|
||||
bool _usesMouseTracking = false;
|
||||
bool _bracketedPasteMode = false;
|
||||
QTimer _bulkTimer1{this};
|
||||
QTimer _bulkTimer2{this};
|
||||
bool _imageSizeInitialized = false;
|
||||
bool _peekingPrimary = false;
|
||||
int _activeScreenIndex = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // ifndef EMULATION_H
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2012 Jekyll Wu <adaptee@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef ENUMERATION_H
|
||||
#define ENUMERATION_H
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
/**
|
||||
* This class serves as a place for putting enum definitions that are
|
||||
* used or referenced in multiple places in the code. Keep it small.
|
||||
*/
|
||||
class Enum
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This enum describes the modes available to remember lines of output
|
||||
* produced by the terminal.
|
||||
*/
|
||||
enum HistoryModeEnum {
|
||||
/** No output is remembered. As soon as lines of text are scrolled
|
||||
* off-screen they are lost.
|
||||
*/
|
||||
NoHistory = 0,
|
||||
/** A fixed number of lines of output are remembered. Once the
|
||||
* limit is reached, the oldest lines are lost.
|
||||
*/
|
||||
FixedSizeHistory = 1,
|
||||
/** All output is remembered for the duration of the session.
|
||||
* Typically this means that lines are recorded to
|
||||
* a file as they are scrolled off-screen.
|
||||
*/
|
||||
UnlimitedHistory = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* This enum describes the positions where the terminal display's
|
||||
* scroll bar may be placed.
|
||||
*/
|
||||
enum ScrollBarPositionEnum {
|
||||
/** Show the scroll-bar on the left of the terminal display. */
|
||||
ScrollBarLeft = 0,
|
||||
/** Show the scroll-bar on the right of the terminal display. */
|
||||
ScrollBarRight = 1,
|
||||
/** Do not show the scroll-bar. */
|
||||
ScrollBarHidden = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* This enum describes the amount that Page Up/Down scroll by.
|
||||
*/
|
||||
enum ScrollPageAmountEnum {
|
||||
/** Scroll half page */
|
||||
ScrollPageHalf = 0,
|
||||
/** Scroll full page */
|
||||
ScrollPageFull = 1,
|
||||
};
|
||||
|
||||
/** This enum describes semantic hints appearance
|
||||
*/
|
||||
enum Hints {
|
||||
HintsNever = 0,
|
||||
HintsURL = 1,
|
||||
HintsAlways = 2,
|
||||
};
|
||||
|
||||
/** This enum describes the shapes used to draw the cursor in terminal
|
||||
* displays.
|
||||
*/
|
||||
enum CursorShapeEnum {
|
||||
/** Use a solid rectangular block to draw the cursor. */
|
||||
BlockCursor = 0,
|
||||
/** Use an 'I' shape, similar to that used in text editing
|
||||
* applications, to draw the cursor.
|
||||
*/
|
||||
IBeamCursor = 1,
|
||||
/** Draw a line underneath the cursor's position. */
|
||||
UnderlineCursor = 2,
|
||||
};
|
||||
|
||||
/** This enum describes the behavior of triple click action . */
|
||||
enum TripleClickModeEnum {
|
||||
/** Select the whole line underneath the cursor. */
|
||||
SelectWholeLine = 0,
|
||||
/** Select from the current cursor position to the end of the line. */
|
||||
SelectForwardsFromCursor = 1,
|
||||
};
|
||||
|
||||
/** This enum describes the source from which mouse middle click pastes data . */
|
||||
enum MiddleClickPasteModeEnum {
|
||||
/** Paste from X11 Selection. */
|
||||
PasteFromX11Selection = 0,
|
||||
/** Paste from Clipboard. */
|
||||
PasteFromClipboard = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* This enum describes the text editor cmd used to open local text file URLs
|
||||
* in Konsole, where line and column data are appended to the file URL, e.g.:
|
||||
* /path/to/file:123:123
|
||||
*/
|
||||
enum TextEditorCmd {
|
||||
Kate = 0,
|
||||
KWrite,
|
||||
KDevelop,
|
||||
QtCreator,
|
||||
Gedit,
|
||||
gVim,
|
||||
CustomTextEditor,
|
||||
Default,
|
||||
};
|
||||
|
||||
/**
|
||||
* This enum describes the different types of sounds and visual effects which
|
||||
* can be used to alert the user when a 'bell' occurs in the terminal
|
||||
* session.
|
||||
*/
|
||||
enum BellModeEnum {
|
||||
/** trigger system beep. */
|
||||
SystemBeepBell = 0,
|
||||
/** trigger system notification. */
|
||||
NotifyBell = 1,
|
||||
/** trigger visual bell(inverting the display's colors briefly). */
|
||||
VisualBell = 2,
|
||||
/** No bell effects */
|
||||
NoBell = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* This enum describes the strategies available for searching
|
||||
* through the session's output.
|
||||
*/
|
||||
enum SearchDirection {
|
||||
/** Searches forwards through the output, starting at the
|
||||
* current selection.
|
||||
*/
|
||||
ForwardsSearch,
|
||||
/** Searches backwards through the output, starting at the
|
||||
* current selection.
|
||||
*/
|
||||
BackwardsSearch,
|
||||
};
|
||||
};
|
||||
}
|
||||
#endif // ENUMERATION_H
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "EscapeSequenceUrlExtractor.h"
|
||||
#include "Screen.h"
|
||||
|
||||
#include <QUrl>
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
EscapeSequenceUrlExtractor::EscapeSequenceUrlExtractor() = default;
|
||||
|
||||
void Konsole::EscapeSequenceUrlExtractor::setScreen(Konsole::Screen *screen)
|
||||
{
|
||||
_screen = screen;
|
||||
clear();
|
||||
}
|
||||
|
||||
void EscapeSequenceUrlExtractor::beginUrlInput()
|
||||
{
|
||||
_reading = true;
|
||||
}
|
||||
|
||||
void EscapeSequenceUrlExtractor::appendUrlText_impl(uint c)
|
||||
{
|
||||
if (_currentUrl.text.isEmpty()) {
|
||||
// We need to on getCursorX because we want the position of the
|
||||
// last printed character, not the cursor.
|
||||
const int realCcolumn = _screen->getCursorY() + _screen->getHistLines();
|
||||
_currentUrl.begin = Coordinate{realCcolumn, _screen->getCursorX() - 1};
|
||||
}
|
||||
_currentUrl.text += QString::fromUcs4(&c, 1);
|
||||
}
|
||||
|
||||
void EscapeSequenceUrlExtractor::setUrl(const QString &url)
|
||||
{
|
||||
if (_allowedUriSchemas.contains(QUrl(url).scheme() + QLatin1String("://"))) {
|
||||
_currentUrl.url = url;
|
||||
} else {
|
||||
abortUrlInput();
|
||||
}
|
||||
}
|
||||
|
||||
void EscapeSequenceUrlExtractor::abortUrlInput()
|
||||
{
|
||||
_reading = false;
|
||||
_currentUrl = ExtractedUrl{};
|
||||
_ignoreNextUrlInput = true;
|
||||
}
|
||||
|
||||
void EscapeSequenceUrlExtractor::endUrlInput()
|
||||
{
|
||||
Q_ASSERT(reading());
|
||||
_reading = false;
|
||||
|
||||
const int realCcolumn = _screen->getCursorY() + _screen->getHistLines();
|
||||
const auto currentPos = Coordinate{realCcolumn, _screen->getCursorX()};
|
||||
_currentUrl.end = currentPos;
|
||||
_history.append(_currentUrl);
|
||||
|
||||
_currentUrl = ExtractedUrl{};
|
||||
}
|
||||
|
||||
void EscapeSequenceUrlExtractor::clear()
|
||||
{
|
||||
_history.clear();
|
||||
}
|
||||
|
||||
void EscapeSequenceUrlExtractor::setAllowedLinkSchema(const QStringList &schema)
|
||||
{
|
||||
_allowedUriSchemas = schema;
|
||||
}
|
||||
|
||||
void EscapeSequenceUrlExtractor::historyLinesRemoved(int lines)
|
||||
{
|
||||
for (auto &url : _history) {
|
||||
url.begin.row -= lines;
|
||||
url.end.row -= lines;
|
||||
}
|
||||
_history.erase(std::remove_if(std::begin(_history),
|
||||
std::end(_history),
|
||||
[](const ExtractedUrl &url) {
|
||||
const bool toRemove = url.begin.row < 0;
|
||||
return toRemove;
|
||||
}),
|
||||
std::end(_history));
|
||||
}
|
||||
|
||||
QVector<ExtractedUrl> EscapeSequenceUrlExtractor::history() const
|
||||
{
|
||||
return _history;
|
||||
}
|
||||
|
||||
void Konsole::EscapeSequenceUrlExtractor::toggleUrlInput()
|
||||
{
|
||||
if (_ignoreNextUrlInput) {
|
||||
_ignoreNextUrlInput = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_reading) {
|
||||
endUrlInput();
|
||||
} else {
|
||||
beginUrlInput();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_EscapeSequenceUrlExtractor.cpp"
|
||||
@@ -0,0 +1,133 @@
|
||||
#ifndef ESCAPE_SEQUENCE_URL_EXTRACTOR_H
|
||||
#define ESCAPE_SEQUENCE_URL_EXTRACTOR_H
|
||||
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "konsoleprivate_export.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class Screen;
|
||||
/* Like QPoint, but with Row / Col
|
||||
* easier to read than x / y
|
||||
*/
|
||||
struct Coordinate {
|
||||
int row;
|
||||
int col;
|
||||
};
|
||||
|
||||
/* Represents a URL on the visual part of konsole, that has been escaped.
|
||||
* like a html url tag with a text value.
|
||||
*/
|
||||
struct ExtractedUrl {
|
||||
QString url;
|
||||
QString text;
|
||||
Coordinate begin;
|
||||
Coordinate end;
|
||||
};
|
||||
|
||||
/* Stored in Screen, but used in V10Emulation to
|
||||
* store extracted URL's. Perhaps this should be a Model?
|
||||
*/
|
||||
class KONSOLEPRIVATE_EXPORT EscapeSequenceUrlExtractor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
/* Tell us if we are currently reading or not a URL. */
|
||||
bool _reading = false;
|
||||
|
||||
/* If we abort reading a URL input we enter in a invalid state,
|
||||
* and we need to ignore the next toggle.
|
||||
*/
|
||||
bool _ignoreNextUrlInput = false;
|
||||
|
||||
/* The url / text pair being extracted currently */
|
||||
ExtractedUrl _currentUrl{};
|
||||
|
||||
/* The maximum size of url to prevent a bomb
|
||||
* that will take over the history file.
|
||||
* TODO: make it configurable.
|
||||
*/
|
||||
// Not used ATM const int _maximumUrlHistory = 200;
|
||||
|
||||
/* All of the extracted URL's. */
|
||||
QVector<ExtractedUrl> _history;
|
||||
|
||||
/* The URI schema format that's accepted */
|
||||
QStringList _allowedUriSchemas;
|
||||
|
||||
/* Pointer to the Screen, that actually holds the text data. */
|
||||
Screen *_screen = nullptr;
|
||||
|
||||
void appendUrlText_impl(uint c);
|
||||
|
||||
public:
|
||||
/* This needs to have access to the Session
|
||||
* calculate the row / col of the current URL.
|
||||
*/
|
||||
EscapeSequenceUrlExtractor();
|
||||
|
||||
/* This is a list of URI schemas that are going to be supported, separated by semicolon.
|
||||
* like https://;file://
|
||||
*/
|
||||
void setAllowedLinkSchema(const QStringList &allowedSchemas);
|
||||
|
||||
void setScreen(Screen *screen);
|
||||
|
||||
/* If we are parsing a URL */
|
||||
bool reading() const
|
||||
{
|
||||
return _reading;
|
||||
}
|
||||
|
||||
/* We found an URL, starting to parse */
|
||||
void beginUrlInput();
|
||||
|
||||
/* We received the end byte to finish the Url. */
|
||||
void endUrlInput();
|
||||
|
||||
/* We are not saving this URL, it's bogus. */
|
||||
void abortUrlInput();
|
||||
|
||||
/* The URL is parsed at once, but not the text. We received
|
||||
* one character per time until we hit the end byte. */
|
||||
void appendUrlText(uint c)
|
||||
{
|
||||
if (!reading()) {
|
||||
return;
|
||||
}
|
||||
appendUrlText_impl(c);
|
||||
}
|
||||
|
||||
/* The URL is parsed at once, store it at once. */
|
||||
void setUrl(const QString &url);
|
||||
|
||||
/* All of the parsedURL's, used by TerminalDisplay to paint them
|
||||
* on screen. */
|
||||
QVector<ExtractedUrl> history() const;
|
||||
|
||||
/* Clear all the URL's, this is triggered when the Screen is cleared. */
|
||||
void clear();
|
||||
|
||||
/* Iterates through all the URL's and remove the ones that are currently
|
||||
* out of bounds because we removed lines in the History
|
||||
*/
|
||||
void historyLinesRemoved(int lines);
|
||||
|
||||
/* starts / stops URL Processing */
|
||||
|
||||
public Q_SLOTS:
|
||||
void toggleUrlInput();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Mariusz Glebocki <mglb@arccos-1.net>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "FontDialog.h"
|
||||
|
||||
// Qt
|
||||
#include <QBoxLayout>
|
||||
#include <QWhatsThis>
|
||||
|
||||
// KDE
|
||||
#include <KLocalizedString>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
FontDialog::FontDialog(QWidget *parent, bool emoji, const QFont font)
|
||||
: QDialog(parent)
|
||||
, _fontChooser(nullptr)
|
||||
, _showAllFonts(nullptr)
|
||||
, _buttonBox(nullptr)
|
||||
, _emoji(emoji)
|
||||
{
|
||||
setWindowTitle(i18nc("@title:window", "Select font"));
|
||||
|
||||
KFontChooser::DisplayFlag onlyFixed = _emoji ? KFontChooser::FixedFontsOnly : KFontChooser::FixedFontsOnly;
|
||||
|
||||
_fontChooser = new KFontChooser(onlyFixed, this);
|
||||
if (_emoji) {
|
||||
QStringList list = KFontChooser::createFontList(0).filter(QStringLiteral("emoji"), Qt::CaseInsensitive);
|
||||
_fontChooser->setFont(font);
|
||||
_fontChooser->setFontListItems(KFontChooser::createFontList(0).filter(QStringLiteral("emoji"), Qt::CaseInsensitive));
|
||||
_fontChooser->setFont(font);
|
||||
}
|
||||
|
||||
_showAllFonts = new QCheckBox(i18nc("@action:button", "Show all fonts"), this);
|
||||
_showAllFontsWarningButton = new QToolButton(this);
|
||||
_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
|
||||
|
||||
if (_emoji) {
|
||||
_fontChooser->setSampleText(
|
||||
/* clang-format off */
|
||||
QStringLiteral(" 🏴🤘🚬🌍🌎🌏🥆💣🗡🔫⚗️⚛️☢️☣️🌿🎱🏧💉💊🕴️📡🤻🦑🇦🇶👩🔬🪤🚱✊🏿🔬🧬🏴☠️🤽\n"
|
||||
"0123456789\n"
|
||||
"👆🏻 👆🏼 👆🏽 👆🏾 👆🏿 👨❤️👨 👨❤️💋👨 👩👩👧👧 👩🏻🤝👨🏿 👨👨👧👦 \U0001F468\u200D\u2764\uFE0F\u200D\U0001F468 \U0001F468\u200D\u2764\u200D\U0001F468\n"
|
||||
"🇧🇲 🇨🇭 🇨🇿 🇪🇺 🇬🇱 🇲🇬 🇲🇹 🇸🇿 🇿🇲"));
|
||||
/* clang-format on */
|
||||
_showAllFonts->hide();
|
||||
_showAllFontsWarningButton->hide();
|
||||
} else {
|
||||
_fontChooser->setSampleText(
|
||||
QStringLiteral("0OQ 1Il!| 5S 8B rnm :; ,. \"'` ~-= ({[<>]})\n"
|
||||
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\n"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789\n"
|
||||
"abcdefghijklmnopqrstuvwxyz"));
|
||||
_showAllFontsWarningButton->setIcon(QIcon::fromTheme(QStringLiteral("emblem-warning")));
|
||||
_showAllFontsWarningButton->setAutoRaise(true);
|
||||
|
||||
connect(_showAllFonts, &QCheckBox::toggled, this, [this](bool enable) {
|
||||
_fontChooser->setFont(_fontChooser->font(), !enable);
|
||||
});
|
||||
connect(_showAllFontsWarningButton, &QToolButton::clicked, this, [this](bool) {
|
||||
const QString message =
|
||||
i18nc("@info:status",
|
||||
"By its very nature, a terminal program requires font characters that are equal width (monospace). Any non monospaced "
|
||||
"font may cause display issues. This should not be necessary except in rare cases.");
|
||||
const QPoint pos = QPoint(_showAllFonts->width() / 2, _showAllFonts->height());
|
||||
QWhatsThis::showText(_showAllFonts->mapToGlobal(pos), message, _showAllFonts);
|
||||
});
|
||||
}
|
||||
|
||||
auto *showAllFontsLayout = new QHBoxLayout();
|
||||
showAllFontsLayout->addWidget(_showAllFonts);
|
||||
showAllFontsLayout->addWidget(_showAllFontsWarningButton);
|
||||
showAllFontsLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));
|
||||
showAllFontsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
showAllFontsLayout->setSpacing(0);
|
||||
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
layout->addWidget(_fontChooser, 1);
|
||||
if (!_emoji) {
|
||||
layout->addLayout(showAllFontsLayout);
|
||||
}
|
||||
layout->addWidget(_buttonBox);
|
||||
connect(_fontChooser, &KFontChooser::fontSelected, this, &FontDialog::fontChanged);
|
||||
connect(_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
void FontDialog::setFont(const QFont &font)
|
||||
{
|
||||
_fontChooser->setFont(font, !_showAllFonts->isChecked() && !_emoji);
|
||||
}
|
||||
|
||||
#include "moc_FontDialog.cpp"
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Mariusz Glebocki <mglb@arccos-1.net>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef FONTDIALOG_H
|
||||
#define FONTDIALOG_H
|
||||
|
||||
// Qt
|
||||
#include <KFontChooser>
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QToolButton>
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class FontDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FontDialog(QWidget *parent = nullptr, bool emoji = false, const QFont font = QFont());
|
||||
|
||||
QFont font() const
|
||||
{
|
||||
return _fontChooser->font();
|
||||
}
|
||||
void setFont(const QFont &font);
|
||||
|
||||
Q_SIGNALS:
|
||||
void fontChanged(const QFont &font);
|
||||
|
||||
private:
|
||||
KFontChooser *_fontChooser;
|
||||
QCheckBox *_showAllFonts;
|
||||
QToolButton *_showAllFontsWarningButton;
|
||||
QDialogButtonBox *_buttonBox;
|
||||
bool _emoji;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // FONTDIALOG_H
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
SPDX-FileCopyrightText: 2012 Kurt Hindenburg <kurt.hindenburg@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "HistorySizeDialog.h"
|
||||
|
||||
// Konsole
|
||||
#include "ui_HistorySizeDialog.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
HistorySizeDialog::HistorySizeDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, _ui(nullptr)
|
||||
{
|
||||
setWindowTitle(i18nc("@title:window", "Adjust Scrollback"));
|
||||
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
auto mainWidget = new QWidget(this);
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
setLayout(mainLayout);
|
||||
mainLayout->addWidget(mainWidget);
|
||||
QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
|
||||
okButton->setDefault(true);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &HistorySizeDialog::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &HistorySizeDialog::reject);
|
||||
mainLayout->addWidget(buttonBox);
|
||||
|
||||
setWindowModality(Qt::WindowModal);
|
||||
|
||||
_ui = new Ui::HistorySizeDialog();
|
||||
_ui->setupUi(mainWidget);
|
||||
|
||||
_ui->tempWarningWidget->setVisible(true);
|
||||
_ui->tempWarningWidget->setWordWrap(false);
|
||||
_ui->tempWarningWidget->setCloseButtonVisible(false);
|
||||
_ui->tempWarningWidget->setMessageType(KMessageWidget::Information);
|
||||
_ui->tempWarningWidget->setText(i18nc("@info:status", "Any adjustments are only temporary to this session."));
|
||||
}
|
||||
|
||||
HistorySizeDialog::~HistorySizeDialog()
|
||||
{
|
||||
delete _ui;
|
||||
}
|
||||
|
||||
void HistorySizeDialog::setMode(Enum::HistoryModeEnum aMode)
|
||||
{
|
||||
_ui->historySizeWidget->setMode(aMode);
|
||||
}
|
||||
|
||||
Enum::HistoryModeEnum HistorySizeDialog::mode() const
|
||||
{
|
||||
return _ui->historySizeWidget->mode();
|
||||
}
|
||||
|
||||
int HistorySizeDialog::lineCount() const
|
||||
{
|
||||
return _ui->historySizeWidget->lineCount();
|
||||
}
|
||||
|
||||
void HistorySizeDialog::setLineCount(int lines)
|
||||
{
|
||||
_ui->historySizeWidget->setLineCount(lines);
|
||||
}
|
||||
|
||||
QSize HistorySizeDialog::sizeHint() const
|
||||
{
|
||||
return {_ui->tempWarningWidget->sizeHint().width(), 0};
|
||||
}
|
||||
|
||||
#include "moc_HistorySizeDialog.cpp"
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
SPDX-FileCopyrightText: 2012 Kurt Hindenburg <kurt.hindenburg@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef HISTORYSIZEDIALOG_H
|
||||
#define HISTORYSIZEDIALOG_H
|
||||
|
||||
// KDE
|
||||
#include <QDialog>
|
||||
|
||||
// Konsole
|
||||
#include "Enumeration.h"
|
||||
#include "konsoleprivate_export.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class HistorySizeDialog;
|
||||
}
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class KONSOLEPRIVATE_EXPORT HistorySizeDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit HistorySizeDialog(QWidget *parent = nullptr);
|
||||
~HistorySizeDialog() override;
|
||||
|
||||
/** See HistorySizeWidget::setMode. */
|
||||
void setMode(Enum::HistoryModeEnum aMode);
|
||||
|
||||
/** See HistorySizeWidget::mode. */
|
||||
Enum::HistoryModeEnum mode() const;
|
||||
|
||||
/** See HistorySizeWidget::setLineCount. */
|
||||
void setLineCount(int lines);
|
||||
|
||||
/** See HistorySizeWidget::lineCount. */
|
||||
int lineCount() const;
|
||||
|
||||
QSize sizeHint() const override;
|
||||
|
||||
private:
|
||||
Ui::HistorySizeDialog *_ui;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // HISTORYSIZEDIALOG_H
|
||||
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>HistorySizeDialog</class>
|
||||
<widget class="QWidget" name="HistorySizeDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>48</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="KMessageWidget" name="tempWarningWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Konsole::HistorySizeWidget" name="historySizeWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Policy::MinimumExpanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>KMessageWidget</class>
|
||||
<extends>QFrame</extends>
|
||||
<header>kmessagewidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>Konsole::HistorySizeWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/HistorySizeWidget.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
// Own
|
||||
#include "KeyBindingEditor.h"
|
||||
|
||||
// Qt
|
||||
#include <QDialogButtonBox>
|
||||
#include <QIcon>
|
||||
#include <QKeyEvent>
|
||||
|
||||
// KDE
|
||||
#include <KLocalizedString>
|
||||
#include <KMessageBox>
|
||||
|
||||
// Konsole
|
||||
#include "keyboardtranslator/KeyboardTranslator.h"
|
||||
#include "keyboardtranslator/KeyboardTranslatorManager.h"
|
||||
#include "keyboardtranslator/KeyboardTranslatorReader.h"
|
||||
#include "ui_KeyBindingEditor.h"
|
||||
|
||||
#include "widgets/EditProfileDialog.h"
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
KeyBindingEditor::KeyBindingEditor(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, _ui(nullptr)
|
||||
, _translator(new KeyboardTranslator(QString()))
|
||||
, _isNewTranslator(false)
|
||||
{
|
||||
auto layout = new QVBoxLayout;
|
||||
|
||||
auto mainWidget = new QWidget(this);
|
||||
layout->addWidget(mainWidget);
|
||||
|
||||
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
buttonBox->button(QDialogButtonBox::Cancel)->setDefault(true);
|
||||
layout->addWidget(buttonBox);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &Konsole::KeyBindingEditor::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
_ui = new Ui::KeyBindingEditor();
|
||||
_ui->setupUi(mainWidget);
|
||||
|
||||
// description edit
|
||||
_ui->descriptionEdit->setPlaceholderText(i18nc("@label:textbox", "Enter descriptive label"));
|
||||
connect(_ui->descriptionEdit, &QLineEdit::textChanged, this, &Konsole::KeyBindingEditor::setTranslatorDescription);
|
||||
// filter edit
|
||||
connect(_ui->filterEdit, &QLineEdit::textChanged, this, &Konsole::KeyBindingEditor::filterRows);
|
||||
|
||||
// key bindings table
|
||||
_ui->keyBindingTable->setColumnCount(2);
|
||||
|
||||
QStringList labels;
|
||||
labels << i18n("Key Combination") << i18n("Output");
|
||||
|
||||
_ui->keyBindingTable->setHorizontalHeaderLabels(labels);
|
||||
_ui->keyBindingTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
||||
|
||||
_ui->keyBindingTable->verticalHeader()->hide();
|
||||
_ui->keyBindingTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
|
||||
// add and remove buttons
|
||||
_ui->addEntryButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
|
||||
_ui->removeEntryButton->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
|
||||
|
||||
connect(_ui->removeEntryButton, &QPushButton::clicked, this, &Konsole::KeyBindingEditor::removeSelectedEntry);
|
||||
connect(_ui->addEntryButton, &QPushButton::clicked, this, &Konsole::KeyBindingEditor::addNewEntry);
|
||||
|
||||
// test area
|
||||
_ui->testAreaInputEdit->installEventFilter(this);
|
||||
}
|
||||
|
||||
KeyBindingEditor::~KeyBindingEditor()
|
||||
{
|
||||
delete _ui;
|
||||
delete _translator;
|
||||
}
|
||||
|
||||
void KeyBindingEditor::filterRows(const QString &text)
|
||||
{
|
||||
const int rows = _ui->keyBindingTable->rowCount();
|
||||
|
||||
QList<int> matchedRows;
|
||||
|
||||
for (QTableWidgetItem *matchedItem : _ui->keyBindingTable->findItems(text, Qt::MatchContains)) {
|
||||
matchedRows.append(matchedItem->row());
|
||||
}
|
||||
|
||||
for (int i = 0; i < rows; i++) {
|
||||
if (matchedRows.contains(i) && _ui->keyBindingTable->isRowHidden(i)) {
|
||||
_ui->keyBindingTable->showRow(i);
|
||||
} else if (!matchedRows.contains(i)) {
|
||||
_ui->keyBindingTable->hideRow(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyBindingEditor::removeSelectedEntry()
|
||||
{
|
||||
QList<QTableWidgetItem *> uniqueList;
|
||||
const QList<QTableWidgetItem *> selectedItems = _ui->keyBindingTable->selectedItems();
|
||||
for (QTableWidgetItem *item : selectedItems) {
|
||||
if (item->column() == 1) { // Select item at the first column
|
||||
item = _ui->keyBindingTable->item(item->row(), 0);
|
||||
}
|
||||
|
||||
if (!uniqueList.contains(item)) {
|
||||
uniqueList.append(item);
|
||||
}
|
||||
}
|
||||
|
||||
for (QTableWidgetItem *item : std::as_const(uniqueList)) {
|
||||
// get the first item in the row which has the entry
|
||||
|
||||
KeyboardTranslator::Entry existing = item->data(Qt::UserRole).value<KeyboardTranslator::Entry>();
|
||||
|
||||
_translator->removeEntry(existing);
|
||||
|
||||
_ui->keyBindingTable->removeRow(item->row());
|
||||
}
|
||||
}
|
||||
|
||||
void KeyBindingEditor::addNewEntry()
|
||||
{
|
||||
_ui->keyBindingTable->insertRow(_ui->keyBindingTable->rowCount());
|
||||
|
||||
int newRowCount = _ui->keyBindingTable->rowCount();
|
||||
|
||||
// block signals here to avoid triggering bindingTableItemChanged() slot call
|
||||
_ui->keyBindingTable->blockSignals(true);
|
||||
|
||||
_ui->keyBindingTable->setItem(newRowCount - 1, 0, new QTableWidgetItem());
|
||||
_ui->keyBindingTable->setItem(newRowCount - 1, 1, new QTableWidgetItem());
|
||||
|
||||
_ui->keyBindingTable->blockSignals(false);
|
||||
|
||||
// make sure user can see new row
|
||||
_ui->keyBindingTable->scrollToItem(_ui->keyBindingTable->item(newRowCount - 1, 0));
|
||||
}
|
||||
|
||||
bool KeyBindingEditor::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (watched == _ui->testAreaInputEdit) {
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
auto *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
|
||||
// The state here is currently set to the state that a newly started
|
||||
// terminal in Konsole will be in ( which is also the same as the
|
||||
// state just after a reset ), this has 'ANSI' turned on and all other
|
||||
// states off.
|
||||
//
|
||||
// TODO: It may be useful to be able to specify the state in the 'test input'
|
||||
// area, but preferably not in a way which clutters the UI with lots of
|
||||
// checkboxes.
|
||||
//
|
||||
const KeyboardTranslator::States states = KeyboardTranslator::AnsiState;
|
||||
|
||||
KeyboardTranslator::Entry entry = _translator->findEntry(keyEvent->key(), keyEvent->modifiers(), states);
|
||||
|
||||
if (!entry.isNull()) {
|
||||
_ui->testAreaInputEdit->setText(entry.conditionToString());
|
||||
_ui->testAreaOutputEdit->setText(entry.resultToString(true, keyEvent->modifiers()));
|
||||
} else {
|
||||
_ui->testAreaInputEdit->setText(keyEvent->text());
|
||||
_ui->testAreaOutputEdit->setText(keyEvent->text());
|
||||
}
|
||||
|
||||
keyEvent->accept();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return QDialog::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void KeyBindingEditor::setDescription(const QString &description)
|
||||
{
|
||||
_ui->descriptionEdit->setText(description);
|
||||
|
||||
setTranslatorDescription(description);
|
||||
}
|
||||
|
||||
void KeyBindingEditor::setTranslatorDescription(const QString &description)
|
||||
{
|
||||
if (_translator != nullptr) {
|
||||
_translator->setDescription(description);
|
||||
}
|
||||
}
|
||||
|
||||
QString KeyBindingEditor::description() const
|
||||
{
|
||||
return _ui->descriptionEdit->text();
|
||||
}
|
||||
|
||||
void KeyBindingEditor::setup(const KeyboardTranslator *translator, const QString ¤tProfileTranslator, bool isNewTranslator)
|
||||
{
|
||||
delete _translator;
|
||||
|
||||
_isNewTranslator = isNewTranslator;
|
||||
|
||||
_currentProfileTranslator = currentProfileTranslator;
|
||||
|
||||
_translator = new KeyboardTranslator(*translator);
|
||||
|
||||
// setup description edit line
|
||||
_ui->descriptionEdit->setClearButtonEnabled(true);
|
||||
// setup filter edit line
|
||||
_ui->filterEdit->setClearButtonEnabled(true);
|
||||
|
||||
if (_isNewTranslator) {
|
||||
setDescription(i18n("New Key Binding List"));
|
||||
setWindowTitle(i18n("New Key Binding List"));
|
||||
} else {
|
||||
_ui->descriptionEdit->setText(translator->description());
|
||||
setWindowTitle(i18n("Edit Key Binding List"));
|
||||
}
|
||||
|
||||
// setup key binding table
|
||||
setupKeyBindingTable(translator);
|
||||
}
|
||||
|
||||
KeyboardTranslator *KeyBindingEditor::translator() const
|
||||
{
|
||||
return _translator;
|
||||
}
|
||||
|
||||
void KeyBindingEditor::bindingTableItemChanged(QTableWidgetItem *item)
|
||||
{
|
||||
QTableWidgetItem *key = _ui->keyBindingTable->item(item->row(), 0);
|
||||
KeyboardTranslator::Entry existing = key->data(Qt::UserRole).value<KeyboardTranslator::Entry>();
|
||||
|
||||
QString condition = key->text();
|
||||
QString result = _ui->keyBindingTable->item(item->row(), 1)->text();
|
||||
|
||||
KeyboardTranslator::Entry entry = KeyboardTranslatorReader::createEntry(condition, result);
|
||||
_translator->replaceEntry(existing, entry);
|
||||
|
||||
// block signals to prevent this slot from being called repeatedly
|
||||
_ui->keyBindingTable->blockSignals(true);
|
||||
|
||||
key->setData(Qt::UserRole, QVariant::fromValue(entry));
|
||||
|
||||
_ui->keyBindingTable->blockSignals(false);
|
||||
}
|
||||
|
||||
void KeyBindingEditor::setupKeyBindingTable(const KeyboardTranslator *translator)
|
||||
{
|
||||
disconnect(_ui->keyBindingTable, &QTableWidget::itemChanged, this, &Konsole::KeyBindingEditor::bindingTableItemChanged);
|
||||
|
||||
QList<KeyboardTranslator::Entry> entries = translator->entries();
|
||||
_ui->keyBindingTable->setRowCount(entries.count());
|
||||
|
||||
for (int row = 0; row < entries.count(); row++) {
|
||||
const KeyboardTranslator::Entry &entry = entries.at(row);
|
||||
|
||||
QTableWidgetItem *keyItem = new QTableWidgetItem(entry.conditionToString());
|
||||
keyItem->setData(Qt::UserRole, QVariant::fromValue(entry));
|
||||
|
||||
QTableWidgetItem *textItem = new QTableWidgetItem(entry.resultToString());
|
||||
|
||||
_ui->keyBindingTable->setItem(row, 0, keyItem);
|
||||
_ui->keyBindingTable->setItem(row, 1, textItem);
|
||||
}
|
||||
_ui->keyBindingTable->sortItems(0);
|
||||
|
||||
connect(_ui->keyBindingTable, &QTableWidget::itemChanged, this, &Konsole::KeyBindingEditor::bindingTableItemChanged);
|
||||
}
|
||||
|
||||
void KeyBindingEditor::accept()
|
||||
{
|
||||
if (_translator == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto newTranslator = new KeyboardTranslator(*_translator);
|
||||
|
||||
if (newTranslator->description().isEmpty()) {
|
||||
KMessageBox::error(this, i18n("A key bindings scheme cannot be saved with an empty description."));
|
||||
delete newTranslator;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isNewTranslator) {
|
||||
newTranslator->setName(newTranslator->description());
|
||||
}
|
||||
|
||||
KeyboardTranslatorManager::instance()->addTranslator(newTranslator);
|
||||
|
||||
const QString ¤tTranslatorName = newTranslator->name();
|
||||
|
||||
Q_EMIT updateKeyBindingsListRequest(currentTranslatorName);
|
||||
|
||||
if (currentTranslatorName == _currentProfileTranslator) {
|
||||
Q_EMIT updateTempProfileKeyBindingsRequest(Profile::KeyBindings, currentTranslatorName);
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
QSize KeyBindingEditor::sizeHint() const
|
||||
{
|
||||
const auto parent = parentWidget();
|
||||
if (parent != nullptr) {
|
||||
return {static_cast<int>(parent->width() * 0.9), static_cast<int>(parent->height() * 0.95)};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
#include "moc_KeyBindingEditor.cpp"
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KEYBINDINGEDITOR_H
|
||||
#define KEYBINDINGEDITOR_H
|
||||
|
||||
// Qt
|
||||
#include <QDialog>
|
||||
|
||||
// Konsole
|
||||
// TODO: Move away Profile::Property from the profile header.
|
||||
#include "profile/Profile.h"
|
||||
|
||||
class QTableWidgetItem;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class KeyBindingEditor;
|
||||
}
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class KeyboardTranslator;
|
||||
|
||||
/**
|
||||
* A dialog which allows the user to edit a key bindings scheme
|
||||
* which maps between key combinations input by the user and
|
||||
* the character sequence sent to the terminal when those
|
||||
* combinations are pressed.
|
||||
*
|
||||
* The dialog can be initialized with the settings of an
|
||||
* existing key bindings scheme using the setup() method.
|
||||
*
|
||||
* The dialog creates a copy of the supplied keyboard translator
|
||||
* to which any changes are applied. The modified translator
|
||||
* can be retrieved using the translator() method.
|
||||
*/
|
||||
class KeyBindingEditor : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/** Constructs a new key bindings editor with the specified parent. */
|
||||
explicit KeyBindingEditor(QWidget *parent = nullptr);
|
||||
~KeyBindingEditor() override;
|
||||
|
||||
/**
|
||||
* Initializes the dialog with the bindings and other settings
|
||||
* from the specified @p translator.
|
||||
* @p currentProfileTranslator the name of the translator set in the
|
||||
* current profile
|
||||
* @p isNewTranslator specifies whether the translator being edited
|
||||
* is an already existing one or a newly created
|
||||
* one, defaults to false.
|
||||
*/
|
||||
void setup(const KeyboardTranslator *translator, const QString ¤tProfileTranslator, bool isNewTranslator = false);
|
||||
|
||||
/**
|
||||
* Returns the modified translator describing the changes to the bindings
|
||||
* and other settings which the user made.
|
||||
*/
|
||||
KeyboardTranslator *translator() const;
|
||||
|
||||
/**
|
||||
* Sets the text of the editor's description field.
|
||||
*/
|
||||
void setDescription(const QString &description);
|
||||
|
||||
/**
|
||||
* Returns the text of the editor's description field.
|
||||
*/
|
||||
QString description() const;
|
||||
|
||||
// reimplemented to handle test area input
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when the user clicks the OK button to save the changes. This
|
||||
* signal is connected to EditProfileDialog::updateKeyBindingsList()
|
||||
* to update the key bindings list on the Keyboard tab in the Edit
|
||||
* Profile dialog.
|
||||
* @p translatorName is the translator that has just been edited
|
||||
*/
|
||||
void updateKeyBindingsListRequest(const QString &translatorName);
|
||||
|
||||
/**
|
||||
* Emitted when the user clicks the OK button to save the changes to
|
||||
* the translator that's set in the current profile; this signal is
|
||||
* connected to EditProfileDialog::updateTempProfileProperty() to
|
||||
* request applying the changes to the _tempProfile.
|
||||
* @p newTranslatorName is the name of the translator, that has just
|
||||
* been edited/saved, and which is also the translator
|
||||
* set in the current Profile
|
||||
*/
|
||||
void updateTempProfileKeyBindingsRequest(Profile::Property, const QString &newTranslatorName);
|
||||
|
||||
private Q_SLOTS:
|
||||
// reimplemented
|
||||
void accept() override;
|
||||
|
||||
void setTranslatorDescription(const QString &description);
|
||||
void bindingTableItemChanged(QTableWidgetItem *item);
|
||||
void removeSelectedEntry();
|
||||
void addNewEntry();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(KeyBindingEditor)
|
||||
|
||||
void setupKeyBindingTable(const KeyboardTranslator *translator);
|
||||
|
||||
Ui::KeyBindingEditor *_ui;
|
||||
|
||||
// translator to which modifications are made as the user makes
|
||||
// changes in the UI.
|
||||
// this is initialized as a copy of the translator specified
|
||||
// when setup() is called
|
||||
KeyboardTranslator *_translator;
|
||||
|
||||
// Show only the rows that match the text entered by the user in the
|
||||
// filter search box
|
||||
void filterRows(const QString &text);
|
||||
|
||||
bool _isNewTranslator;
|
||||
|
||||
// The translator set in the current profile
|
||||
QString _currentProfileTranslator;
|
||||
|
||||
// Sets the size hint of the dialog to be slightly smaller than the
|
||||
// size of EditProfileDialog
|
||||
QSize sizeHint() const override;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // KEYBINDINGEDITOR_H
|
||||
@@ -0,0 +1,149 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>KeyBindingEditor</class>
|
||||
<widget class="QWidget" name="KeyBindingEditor">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>374</width>
|
||||
<height>530</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="descriptionLabel">
|
||||
<property name="text">
|
||||
<string>Description:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="descriptionEdit"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="filterLabel">
|
||||
<property name="text">
|
||||
<string>Filter:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="filterEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="keyBindingTable">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="addEntryButton">
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="removeEntryButton">
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string comment="Title of the area where you test your keys are properly configured">Test Area</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Input:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="testAreaInputEdit"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Output:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="testAreaOutputEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
SPDX-FileCopyrightText: 2018 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "LabelsAligner.h"
|
||||
|
||||
// Qt
|
||||
#include <QGridLayout>
|
||||
#include <QWidget>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
LabelsAligner::LabelsAligner(QWidget *refWidget)
|
||||
: _refWidget(refWidget)
|
||||
{
|
||||
}
|
||||
|
||||
void LabelsAligner::addLayout(QGridLayout *layout)
|
||||
{
|
||||
_layouts.append(layout);
|
||||
}
|
||||
|
||||
void LabelsAligner::addLayouts(const QVector<QGridLayout *> &layouts)
|
||||
{
|
||||
_layouts.append(layouts);
|
||||
}
|
||||
|
||||
void LabelsAligner::setReferenceWidget(QWidget *refWidget)
|
||||
{
|
||||
_refWidget = refWidget;
|
||||
}
|
||||
|
||||
void LabelsAligner::updateLayouts()
|
||||
{
|
||||
for (const auto *layout : std::as_const(_layouts)) {
|
||||
QWidget *widget = layout->parentWidget();
|
||||
Q_ASSERT(widget);
|
||||
do {
|
||||
QLayout *widgetLayout = widget->layout();
|
||||
if (widgetLayout != nullptr) {
|
||||
widgetLayout->update();
|
||||
widgetLayout->activate();
|
||||
}
|
||||
widget = widget->parentWidget();
|
||||
} while (widget != _refWidget && widget != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void LabelsAligner::align()
|
||||
{
|
||||
Q_ASSERT(_refWidget);
|
||||
|
||||
if (_layouts.count() <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
int maxRight = 0;
|
||||
for (const auto *layout : std::as_const(_layouts)) {
|
||||
int left = getLeftMargin(layout);
|
||||
for (int row = 0; row < layout->rowCount(); ++row) {
|
||||
QLayoutItem *layoutItem = layout->itemAtPosition(row, LABELS_COLUMN);
|
||||
if (layoutItem == nullptr) {
|
||||
continue;
|
||||
}
|
||||
QWidget *widget = layoutItem->widget();
|
||||
if (widget == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const int idx = layout->indexOf(widget);
|
||||
int rows, cols, rowSpan, colSpan;
|
||||
layout->getItemPosition(idx, &rows, &cols, &rowSpan, &colSpan);
|
||||
if (colSpan > 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int right = left + widget->sizeHint().width();
|
||||
if (maxRight < right) {
|
||||
maxRight = right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto *l : std::as_const(_layouts)) {
|
||||
int left = getLeftMargin(l);
|
||||
l->setColumnMinimumWidth(LABELS_COLUMN, maxRight - left);
|
||||
}
|
||||
}
|
||||
|
||||
int LabelsAligner::getLeftMargin(const QGridLayout *layout)
|
||||
{
|
||||
int left = layout->contentsMargins().left();
|
||||
|
||||
if (layout->parent()->isWidgetType()) {
|
||||
auto *parentWidget = layout->parentWidget();
|
||||
Q_ASSERT(parentWidget);
|
||||
left += parentWidget->contentsMargins().left();
|
||||
} else {
|
||||
auto *parentLayout = qobject_cast<QLayout *>(layout->parent());
|
||||
Q_ASSERT(parentLayout);
|
||||
left += parentLayout->contentsMargins().left();
|
||||
}
|
||||
|
||||
QWidget *parent = layout->parentWidget();
|
||||
while (parent != _refWidget && parent != nullptr) {
|
||||
left = parent->mapToParent(QPoint(left, 0)).x();
|
||||
parent = parent->parentWidget();
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
#include "moc_LabelsAligner.cpp"
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
SPDX-FileCopyrightText: 2018 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef LABELSALIGNER_H
|
||||
#define LABELSALIGNER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
|
||||
class QWidget;
|
||||
class QGridLayout;
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
/**
|
||||
* An utility class for aligning 0th column in multiple QGridLayouts.
|
||||
*
|
||||
* Limitations:
|
||||
* - a layout can't be nested in another layout
|
||||
* - reference widget must be an ancestor of all added layouts
|
||||
* - only 0th column is processed (widgets spanning multiple columns
|
||||
* are ignored)
|
||||
*/
|
||||
class LabelsAligner : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LabelsAligner(QWidget *refWidget);
|
||||
|
||||
void addLayout(QGridLayout *layout);
|
||||
void addLayouts(const QVector<QGridLayout *> &layouts);
|
||||
void setReferenceWidget(QWidget *refWidget);
|
||||
|
||||
public Q_SLOTS:
|
||||
void updateLayouts();
|
||||
void align();
|
||||
|
||||
private:
|
||||
int getLeftMargin(const QGridLayout *layout);
|
||||
|
||||
static constexpr int LABELS_COLUMN = 0;
|
||||
|
||||
QWidget *_refWidget;
|
||||
QVector<QGridLayout *> _layouts;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
// Qt
|
||||
#include <QAction>
|
||||
#include <QExplicitlySharedDataPointer>
|
||||
#include <QPointer>
|
||||
#include <QUrl>
|
||||
|
||||
// KDE
|
||||
#include <KXmlGuiWindow>
|
||||
|
||||
// Konsole
|
||||
#include "widgets/ViewSplitter.h"
|
||||
|
||||
#include "pluginsystem/IKonsolePlugin.h"
|
||||
|
||||
#include "konsoleapp_export.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class KActionMenu;
|
||||
class KHamburgerMenu;
|
||||
class KToggleAction;
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class ViewManager;
|
||||
class ViewProperties;
|
||||
class Session;
|
||||
class SessionController;
|
||||
class Profile;
|
||||
class ProfileList;
|
||||
class BookmarkHandler;
|
||||
|
||||
/**
|
||||
* The main window. This contains the menus and an area which contains the terminal displays.
|
||||
*
|
||||
* The main window does not create the views or the container widgets which hold the views.
|
||||
* This is done by the ViewManager class. When a MainWindow is instantiated, it creates
|
||||
* a new ViewManager. The ViewManager can then be used to create new terminal displays
|
||||
* inside the window.
|
||||
*
|
||||
* Do not construct new main windows directly, use Application's newMainWindow() method.
|
||||
*/
|
||||
class KONSOLEAPP_EXPORT MainWindow : public KXmlGuiWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a new main window. Do not create new main windows directly, use Application's
|
||||
* newMainWindow() method instead.
|
||||
*/
|
||||
MainWindow();
|
||||
|
||||
/**
|
||||
* Returns the view manager associated with this window. The view manager can be used to
|
||||
* create new views on particular session objects inside this window.
|
||||
*/
|
||||
ViewManager *viewManager() const;
|
||||
|
||||
/**
|
||||
* Create a new session.
|
||||
*
|
||||
* @param profile The profile to use to create the new session.
|
||||
* @param directory Initial working directory for the new session or empty
|
||||
* if the default working directory associated with the profile should be used.
|
||||
*/
|
||||
Session *createSession(QExplicitlySharedDataPointer<Profile> profile, const QString &directory);
|
||||
|
||||
/**
|
||||
* create a new SSH session.
|
||||
*
|
||||
* @param profile The profile to use to create the new session.
|
||||
* @param url the URL representing the new SSH connection
|
||||
*/
|
||||
Session *createSSHSession(QExplicitlySharedDataPointer<Profile> profile, const QUrl &url);
|
||||
|
||||
/**
|
||||
* Helper method to make this window get input focus
|
||||
*/
|
||||
void setFocus();
|
||||
|
||||
/**
|
||||
* Set the initial visibility of the menubar.
|
||||
*/
|
||||
void setMenuBarInitialVisibility(bool showMenuBar);
|
||||
|
||||
/**
|
||||
* @brief Set the frameless state
|
||||
*
|
||||
* @param frameless If true, no titlebar or frame is displayed.
|
||||
*/
|
||||
void setRemoveWindowTitleBarAndFrame(bool frameless);
|
||||
|
||||
/**
|
||||
* A reference to a plugin on the system.
|
||||
*/
|
||||
void addPlugin(IKonsolePlugin *plugin);
|
||||
|
||||
/**
|
||||
* creates a new tab for the main window
|
||||
*/
|
||||
void newTab();
|
||||
|
||||
/**
|
||||
* @brief set list actions for menu "Plugins"
|
||||
*/
|
||||
void setPluginsActions(const QList<QAction *> &actions);
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
/**
|
||||
* Emitted by the main window to request the creation of a
|
||||
* new session in a new window.
|
||||
*
|
||||
* @param profile The profile to use to create the
|
||||
* first session in the new window.
|
||||
* @param directory Initial working directory for the new window or empty
|
||||
* if the default working directory associated with the profile should
|
||||
* be used.
|
||||
*/
|
||||
void newWindowRequest(const QExplicitlySharedDataPointer<Profile> &profile, const QString &directory);
|
||||
|
||||
/**
|
||||
* Emitted when a view for one session is detached from this window
|
||||
*/
|
||||
void terminalsDetached(ViewSplitter *splitter, QHash<TerminalDisplay *, Session *> sessionsMap);
|
||||
|
||||
protected:
|
||||
// Reimplemented for internal reasons.
|
||||
void showEvent(QShowEvent *event) override;
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
|
||||
// reimplemented from KMainWindow
|
||||
bool queryClose() override;
|
||||
void saveProperties(KConfigGroup &group) override;
|
||||
void readProperties(const KConfigGroup &group) override;
|
||||
void saveGlobalProperties(KConfig *config) override;
|
||||
void readGlobalProperties(KConfig *config) override;
|
||||
|
||||
// reimplemented from QWidget
|
||||
bool focusNextPrevChild(bool next) override;
|
||||
|
||||
protected Q_SLOTS:
|
||||
void saveNewToolbarConfig() override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void cloneTab();
|
||||
void newWindow();
|
||||
void showManageProfilesDialog();
|
||||
void activateMenuBar();
|
||||
void showSettingsDialog(bool showProfilePage = false);
|
||||
void showShortcutsDialog();
|
||||
void newFromProfile(const QExplicitlySharedDataPointer<Profile> &profile);
|
||||
void activeViewChanged(SessionController *controller);
|
||||
void disconnectController(SessionController *controller);
|
||||
void activeViewTitleChanged(ViewProperties *);
|
||||
|
||||
void profileListChanged(const QList<QAction *> &sessionActions);
|
||||
void configureNotifications();
|
||||
void setBlur(bool blur);
|
||||
|
||||
void updateWindowIcon();
|
||||
void updateWindowCaption();
|
||||
void openUrls(const QList<QUrl> &urls);
|
||||
|
||||
// Sets the list of profiles to be displayed under the "New Tab" action
|
||||
void setProfileList(ProfileList *list);
|
||||
|
||||
void applyKonsoleSettings();
|
||||
|
||||
// Ask the window manager to show this application window
|
||||
void activationRequest(const QString &xdgActivationToken);
|
||||
|
||||
void updateUseTransparency();
|
||||
|
||||
public Q_SLOTS:
|
||||
void viewFullScreen(bool fullScreen);
|
||||
|
||||
private:
|
||||
void applyMainWindowSettings(const KConfigGroup &config) override;
|
||||
|
||||
/**
|
||||
* Returns true if the window geometry was previously saved to the
|
||||
* config file, false otherwise.
|
||||
*/
|
||||
bool wasWindowGeometrySaved() const;
|
||||
|
||||
void correctStandardShortcuts();
|
||||
void rememberMenuAccelerators();
|
||||
void removeMenuAccelerators();
|
||||
void restoreMenuAccelerators();
|
||||
void setupActions();
|
||||
QString activeSessionDir() const;
|
||||
void triggerAction(const QString &name) const;
|
||||
|
||||
/**
|
||||
* Returns the bookmark handler associated with this window.
|
||||
*/
|
||||
BookmarkHandler *bookmarkHandler() const;
|
||||
|
||||
// sets the active shortcuts of actions in 'dest' to the shortcuts of actions
|
||||
// with the same name in 'source' (see QAction::ActiveShortcut)
|
||||
static void syncActiveShortcuts(KActionCollection *dest, const KActionCollection *source);
|
||||
void updateHamburgerMenu();
|
||||
|
||||
private:
|
||||
ViewManager *_viewManager;
|
||||
BookmarkHandler *_bookmarkHandler;
|
||||
KToggleAction *_toggleMenuBarAction;
|
||||
KActionMenu *_newTabMenuAction;
|
||||
KHamburgerMenu *_hamburgerMenu;
|
||||
|
||||
QPointer<SessionController> _pluggedController;
|
||||
std::vector<IKonsolePlugin *> _plugins;
|
||||
QList<QAction *> _pluginsActions;
|
||||
bool _blurEnabled = false;
|
||||
bool _firstShowEvent = true;
|
||||
|
||||
struct {
|
||||
bool enabled = false; // indicates that we got a command line argument for menubar
|
||||
bool showMenuBar = true;
|
||||
} _windowArgsMenuBarVisible;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
@@ -0,0 +1,15 @@
|
||||
#! /bin/sh
|
||||
rm -f schemas.cpp
|
||||
for i in ../data/color-schemes/*.colorscheme; do
|
||||
grep "^Description=" $i | sed -e 's#^Description=\(.*\)$#i18n(\"\1\")#' >> schemas.cpp
|
||||
done
|
||||
for i in ../data/keyboard-layouts/*.keytab; do
|
||||
grep "^keyboard" $i | sed -e 's#^keyboard \"\(.*\)\"$#i18n(\"\1\")#' >> schemas.cpp
|
||||
done
|
||||
$EXTRACTRC `find . -name \*.ui` >> rc.cpp
|
||||
$EXTRACTRC `find . -name \*.kcfg` >> rc.cpp
|
||||
$EXTRACTRC `find ../desktop -name \*.rc` >> rc.cpp
|
||||
$XGETTEXT `find . -name \*.cpp -o -name \*.h | grep -v '/tests/'` -o $podir/konsole.pot
|
||||
rm -f schemas.cpp
|
||||
#rm -f tips.cpp
|
||||
rm -f rc.cpp
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.countm>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "NullProcessInfo.h"
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
NullProcessInfo::NullProcessInfo(int pid)
|
||||
: ProcessInfo(pid)
|
||||
{
|
||||
}
|
||||
|
||||
void NullProcessInfo::readProcessInfo(int /*pid*/)
|
||||
{
|
||||
}
|
||||
|
||||
bool NullProcessInfo::readProcessName(int /*pid*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NullProcessInfo::readCurrentDir(int /*pid*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NullProcessInfo::readArguments(int /*pid*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void NullProcessInfo::readUserName()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.countm>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef NULLPROCESSINFO_H
|
||||
#define NULLPROCESSINFO_H
|
||||
|
||||
#include "ProcessInfo.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
/**
|
||||
* Implementation of ProcessInfo which does nothing.
|
||||
* Used on platforms where a suitable ProcessInfo subclass is not
|
||||
* available.
|
||||
*
|
||||
* isValid() will always return false for instances of NullProcessInfo
|
||||
*/
|
||||
class NullProcessInfo : public ProcessInfo
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructs a new NullProcessInfo instance.
|
||||
* See ProcessInfo::newInstance()
|
||||
*/
|
||||
explicit NullProcessInfo(int pid);
|
||||
|
||||
protected:
|
||||
void readProcessInfo(int pid) override;
|
||||
bool readProcessName(int pid) override;
|
||||
bool readCurrentDir(int pid) override;
|
||||
bool readArguments(int pid) override;
|
||||
void readUserName(void) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "Part.h"
|
||||
|
||||
// Qt
|
||||
#include <QDir>
|
||||
#include <QKeyEvent>
|
||||
#include <QMetaEnum>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
|
||||
// KDE
|
||||
#include <KActionCollection>
|
||||
#include <KConfigDialog>
|
||||
#include <KLocalizedString>
|
||||
#include <KPluginFactory>
|
||||
#include <QAction>
|
||||
|
||||
// Konsole
|
||||
#include "Emulation.h"
|
||||
#include "KonsoleSettings.h"
|
||||
#include "ViewManager.h"
|
||||
#include "profile/ProfileManager.h"
|
||||
#include "session/SessionController.h"
|
||||
#include "session/SessionManager.h"
|
||||
#include "settings/PartInfo.h"
|
||||
#include "settings/ProfileSettings.h"
|
||||
#include "terminalDisplay/TerminalDisplay.h"
|
||||
#include "widgets/EditProfileDialog.h"
|
||||
#include "widgets/ViewContainer.h"
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
K_PLUGIN_FACTORY_WITH_JSON(KonsolePartFactory, "konsolepart.json", registerPlugin<Konsole::Part>();)
|
||||
|
||||
Part::Part(QObject *parent, const QVariantList &)
|
||||
: KParts::ReadOnlyPart(parent)
|
||||
, _viewManager(nullptr)
|
||||
, _pluggedController(nullptr)
|
||||
{
|
||||
// create view widget
|
||||
_viewManager = new ViewManager(this, actionCollection());
|
||||
_viewManager->setNavigationMethod(ViewManager::NoNavigation);
|
||||
|
||||
connect(_viewManager, &Konsole::ViewManager::activeViewChanged, this, &Konsole::Part::activeViewChanged);
|
||||
connect(_viewManager, &Konsole::ViewManager::empty, this, &Konsole::Part::terminalExited);
|
||||
connect(_viewManager, &Konsole::ViewManager::newViewRequest, this, &Konsole::Part::newTab);
|
||||
|
||||
_viewManager->widget()->setParent(widget());
|
||||
|
||||
setWidget(_viewManager->widget());
|
||||
actionCollection()->addAssociatedWidget(_viewManager->widget());
|
||||
const QList<QAction *> actionsList = actionCollection()->actions();
|
||||
for (QAction *action : actionsList) {
|
||||
action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||||
}
|
||||
|
||||
// Enable translucency support if supported by the app.
|
||||
if (_viewManager->widget()->window() && _viewManager->widget()->window()->testAttribute(Qt::WA_TranslucentBackground)) {
|
||||
_viewManager->widget()->setAttribute(Qt::WA_TranslucentBackground, true);
|
||||
}
|
||||
|
||||
// create basic session
|
||||
createSession();
|
||||
}
|
||||
|
||||
Part::~Part() = default;
|
||||
|
||||
bool Part::openFile()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Part::terminalExited()
|
||||
{
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void Part::newTab()
|
||||
{
|
||||
createSession();
|
||||
}
|
||||
|
||||
Session *Part::activeSession() const
|
||||
{
|
||||
if (_viewManager->activeViewController() != nullptr) {
|
||||
Q_ASSERT(_viewManager->activeViewController()->session());
|
||||
|
||||
return _viewManager->activeViewController()->session();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Part::startProgram(const QString &program, const QStringList &arguments)
|
||||
{
|
||||
Q_ASSERT(activeSession());
|
||||
|
||||
// do nothing if the session has already started running
|
||||
if (activeSession()->isRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!program.isEmpty() && !arguments.isEmpty()) {
|
||||
activeSession()->setProgram(program);
|
||||
activeSession()->setArguments(arguments);
|
||||
}
|
||||
|
||||
activeSession()->run();
|
||||
}
|
||||
|
||||
void Part::openTeletype(int ptyMasterFd, bool runShell)
|
||||
{
|
||||
Q_ASSERT(activeSession());
|
||||
|
||||
activeSession()->openTeletype(ptyMasterFd, runShell);
|
||||
}
|
||||
|
||||
void Part::showShellInDir(const QString &dir)
|
||||
{
|
||||
Q_ASSERT(activeSession());
|
||||
|
||||
// do nothing if the session has already started running
|
||||
if (activeSession()->isRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// All other checking is done in setInitialWorkingDirectory()
|
||||
if (!dir.isEmpty()) {
|
||||
activeSession()->setInitialWorkingDirectory(dir);
|
||||
}
|
||||
|
||||
activeSession()->run();
|
||||
}
|
||||
|
||||
void Part::sendInput(const QString &text)
|
||||
{
|
||||
Q_ASSERT(activeSession());
|
||||
activeSession()->sendTextToTerminal(text);
|
||||
}
|
||||
|
||||
int Part::terminalProcessId()
|
||||
{
|
||||
Q_ASSERT(activeSession());
|
||||
|
||||
return activeSession()->processId();
|
||||
}
|
||||
|
||||
int Part::foregroundProcessId()
|
||||
{
|
||||
Q_ASSERT(activeSession());
|
||||
|
||||
if (activeSession()->isForegroundProcessActive()) {
|
||||
return activeSession()->foregroundProcessId();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString Part::foregroundProcessName()
|
||||
{
|
||||
Q_ASSERT(activeSession());
|
||||
|
||||
if (activeSession()->isForegroundProcessActive()) {
|
||||
return activeSession()->foregroundProcessName();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString Part::currentWorkingDirectory() const
|
||||
{
|
||||
Q_ASSERT(activeSession());
|
||||
|
||||
return activeSession()->currentWorkingDirectory();
|
||||
}
|
||||
|
||||
QVariant Part::profileProperty(const QString &profileProperty) const
|
||||
{
|
||||
const auto metaEnum = QMetaEnum::fromType<Profile::Property>();
|
||||
const auto value = metaEnum.keyToValue(profileProperty.toStdString().c_str());
|
||||
|
||||
if (value == -1) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
const auto p = static_cast<Profile::Property>(value);
|
||||
return SessionManager::instance()->sessionProfile(activeSession())->property<QVariant>(p);
|
||||
}
|
||||
|
||||
QStringList Part::availableProfiles() const
|
||||
{
|
||||
return ProfileManager::instance()->availableProfileNames();
|
||||
}
|
||||
|
||||
QString Part::currentProfileName() const
|
||||
{
|
||||
return SessionManager::instance()->sessionProfile(activeSession())->name();
|
||||
}
|
||||
|
||||
bool Part::setCurrentProfile(const QString &profileName)
|
||||
{
|
||||
Profile::Ptr profile;
|
||||
for (auto p : ProfileManager::instance()->allProfiles()) {
|
||||
if (p->name() == profileName) {
|
||||
profile = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!profile) {
|
||||
profile = ProfileManager::instance()->loadProfile(profileName);
|
||||
}
|
||||
|
||||
SessionManager::instance()->setSessionProfile(activeSession(), profile);
|
||||
return currentProfileName() == profileName;
|
||||
}
|
||||
|
||||
void Part::createSession(const QString &profileName, const QString &directory)
|
||||
{
|
||||
Profile::Ptr profile = ProfileManager::instance()->defaultProfile();
|
||||
if (!profileName.isEmpty()) {
|
||||
profile = ProfileManager::instance()->loadProfile(profileName);
|
||||
}
|
||||
|
||||
Q_ASSERT(profile);
|
||||
|
||||
Session *session = SessionManager::instance()->createSession(profile);
|
||||
|
||||
// override the default directory specified in the profile
|
||||
if (!directory.isEmpty() && profile->startInCurrentSessionDir()) {
|
||||
session->setInitialWorkingDirectory(directory);
|
||||
}
|
||||
|
||||
auto newView = _viewManager->createView(session);
|
||||
_viewManager->activeContainer()->addView(newView);
|
||||
}
|
||||
|
||||
void Part::activeViewChanged(SessionController *controller)
|
||||
{
|
||||
Q_ASSERT(controller);
|
||||
Q_ASSERT(controller->view());
|
||||
|
||||
// remove existing controller
|
||||
if (_pluggedController != nullptr) {
|
||||
removeChildClient(_pluggedController);
|
||||
disconnect(_pluggedController, &Konsole::SessionController::titleChanged, this, &Konsole::Part::activeViewTitleChanged);
|
||||
disconnect(_pluggedController, &Konsole::SessionController::currentDirectoryChanged, this, &Konsole::Part::currentDirectoryChanged);
|
||||
}
|
||||
|
||||
// insert new controller
|
||||
insertChildClient(controller);
|
||||
|
||||
connect(controller, &Konsole::SessionController::titleChanged, this, &Konsole::Part::activeViewTitleChanged);
|
||||
activeViewTitleChanged(controller);
|
||||
connect(controller, &Konsole::SessionController::currentDirectoryChanged, this, &Konsole::Part::currentDirectoryChanged);
|
||||
|
||||
disconnect(controller->view(), &TerminalDisplay::overrideShortcutCheck, this, &Part::overrideTerminalShortcut);
|
||||
connect(controller->view(), &TerminalDisplay::overrideShortcutCheck, this, &Part::overrideTerminalShortcut);
|
||||
|
||||
_pluggedController = controller;
|
||||
}
|
||||
|
||||
void Part::overrideTerminalShortcut(QKeyEvent *event, bool &override)
|
||||
{
|
||||
// Shift+Insert is commonly used as the alternate shortcut for
|
||||
// pasting in KDE apps(including konsole), so it deserves some
|
||||
// special treatment.
|
||||
if (((event->modifiers() & Qt::ShiftModifier) != 0U) && (event->key() == Qt::Key_Insert)) {
|
||||
override = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// override all shortcuts in the embedded terminal by default
|
||||
override = true;
|
||||
Q_EMIT overrideShortcut(event, override);
|
||||
}
|
||||
|
||||
void Part::activeViewTitleChanged(ViewProperties *properties)
|
||||
{
|
||||
Q_EMIT setWindowCaption(properties->title());
|
||||
}
|
||||
|
||||
void Part::showManageProfilesDialog(QWidget *parent)
|
||||
{
|
||||
// Make sure this string is unique among all users of this part
|
||||
if (KConfigDialog::showDialog(QStringLiteral("konsolepartmanageprofiles"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
KConfigDialog *settingsDialog = new KConfigDialog(parent, QStringLiteral("konsolepartmanageprofiles"), KonsoleSettings::self());
|
||||
settingsDialog->setFaceType(KPageDialog::Tabbed);
|
||||
|
||||
auto profileSettings = new ProfileSettings(settingsDialog);
|
||||
settingsDialog->addPage(profileSettings, i18nc("@title Preferences page name", "Profiles"), QStringLiteral("configure"));
|
||||
|
||||
auto partInfoSettings = new PartInfoSettings(settingsDialog);
|
||||
settingsDialog->addPage(partInfoSettings, i18nc("@title Preferences page name", "Part Info"), QStringLiteral("dialog-information"));
|
||||
|
||||
settingsDialog->show();
|
||||
}
|
||||
|
||||
void Part::showEditCurrentProfileDialog(QWidget *parent)
|
||||
{
|
||||
Q_ASSERT(activeSession());
|
||||
|
||||
auto dialog = new EditProfileDialog(parent);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dialog->setProfile(SessionManager::instance()->sessionProfile(activeSession()));
|
||||
dialog->show();
|
||||
}
|
||||
|
||||
void Part::changeSessionSettings(const QString &text)
|
||||
{
|
||||
Q_ASSERT(activeSession());
|
||||
|
||||
// send a profile change command, the escape code format
|
||||
// is the same as the normal X-Term commands used to change the window title or icon,
|
||||
// but with a magic value of '50' for the parameter which specifies what to change
|
||||
QString command = QStringLiteral("\033]50;%1\a").arg(text);
|
||||
|
||||
sendInput(command);
|
||||
}
|
||||
|
||||
// Konqueror integration
|
||||
bool Part::openUrl(const QUrl &url)
|
||||
{
|
||||
if (KParts::ReadOnlyPart::url() == url) {
|
||||
Q_EMIT completed();
|
||||
return true;
|
||||
}
|
||||
|
||||
setUrl(url);
|
||||
Q_EMIT setWindowCaption(url.toDisplayString(QUrl::PreferLocalFile));
|
||||
////qDebug() << "Set Window Caption to " << url.pathOrUrl();
|
||||
Q_EMIT started(nullptr);
|
||||
|
||||
if (url.isLocalFile()) {
|
||||
showShellInDir(url.path());
|
||||
} else {
|
||||
showShellInDir(QDir::homePath());
|
||||
}
|
||||
|
||||
Q_EMIT completed();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Part::setMonitorSilenceEnabled(bool enabled)
|
||||
{
|
||||
Q_ASSERT(activeSession());
|
||||
|
||||
if (enabled) {
|
||||
activeSession()->setMonitorSilence(true);
|
||||
connect(activeSession(), &Konsole::Session::notificationsChanged, this, &Konsole::Part::notificationChanged, Qt::UniqueConnection);
|
||||
} else {
|
||||
activeSession()->setMonitorSilence(false);
|
||||
if (!activeSession()->isMonitorActivity()) {
|
||||
disconnect(activeSession(), &Konsole::Session::notificationsChanged, this, &Konsole::Part::notificationChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Part::setMonitorActivityEnabled(bool enabled)
|
||||
{
|
||||
Q_ASSERT(activeSession());
|
||||
|
||||
if (enabled) {
|
||||
activeSession()->setMonitorActivity(true);
|
||||
connect(activeSession(), &Konsole::Session::notificationsChanged, this, &Konsole::Part::notificationChanged, Qt::UniqueConnection);
|
||||
} else {
|
||||
activeSession()->setMonitorActivity(false);
|
||||
if (!activeSession()->isMonitorSilence()) {
|
||||
disconnect(activeSession(), &Konsole::Session::notificationsChanged, this, &Konsole::Part::notificationChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Part::isBlurEnabled()
|
||||
{
|
||||
return ViewManager::profileHasBlurEnabled(SessionManager::instance()->sessionProfile(activeSession()));
|
||||
}
|
||||
|
||||
void Part::notificationChanged(Session::Notification notification, bool enabled)
|
||||
{
|
||||
if (notification == Session::Notification::Silence && enabled) {
|
||||
Q_EMIT silenceDetected();
|
||||
} else if (notification == Session::Notification::Activity && enabled) {
|
||||
Q_EMIT activityDetected();
|
||||
}
|
||||
}
|
||||
|
||||
#include "Part.moc"
|
||||
#include "moc_Part.cpp"
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef PART_H
|
||||
#define PART_H
|
||||
|
||||
// KDE
|
||||
#include <KParts/ReadOnlyPart>
|
||||
#include <kde_terminal_interface.h>
|
||||
|
||||
// Qt
|
||||
#include <QVariantList>
|
||||
|
||||
// Konsole
|
||||
#include "config-konsole.h"
|
||||
#include "session/Session.h"
|
||||
|
||||
class QKeyEvent;
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class Session;
|
||||
class SessionController;
|
||||
class ViewManager;
|
||||
class ViewProperties;
|
||||
|
||||
/**
|
||||
* A re-usable terminal emulator component using the KParts framework which can
|
||||
* be used to embed terminal emulators into other applications.
|
||||
*/
|
||||
class Part : public KParts::ReadOnlyPart, public TerminalInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(TerminalInterface)
|
||||
public:
|
||||
/** Constructs a new Konsole part with the specified parent. */
|
||||
explicit Part(QObject *parent, const QVariantList &);
|
||||
~Part() override;
|
||||
|
||||
/** Reimplemented from TerminalInterface. */
|
||||
void startProgram(const QString &program, const QStringList &arguments) override;
|
||||
/** Reimplemented from TerminalInterface. */
|
||||
void showShellInDir(const QString &dir) override;
|
||||
/** Reimplemented from TerminalInterface. */
|
||||
void sendInput(const QString &text) override;
|
||||
|
||||
/** Reimplemented from TerminalInterface. */
|
||||
int terminalProcessId() override;
|
||||
|
||||
/** Reimplemented from TerminalInterface. */
|
||||
int foregroundProcessId() override;
|
||||
|
||||
/** Reimplemented from TerminalInterface. */
|
||||
QString foregroundProcessName() override;
|
||||
|
||||
/** Reimplemented from TerminalInterface. */
|
||||
QString currentWorkingDirectory() const override;
|
||||
|
||||
/** Reimplemented from TerminalInterfaceV2 */
|
||||
QStringList availableProfiles() const override;
|
||||
|
||||
/** Reimplemented from TerminalInterfaceV2 */
|
||||
QString currentProfileName() const override;
|
||||
|
||||
/** Reimplemented from TerminalInterfaceV2 */
|
||||
bool setCurrentProfile(const QString &profileName) override;
|
||||
|
||||
/** Reimplemented from TerminalInterfaceV2 */
|
||||
QVariant profileProperty(const QString &profileProperty) const override;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* creates and run a session using the specified profile and directory
|
||||
*
|
||||
* @param profileName Specifies the name of the profile to create session
|
||||
* @param directory specifies The initial working directory of the created session
|
||||
*
|
||||
* This is highly experimental. Do not use it at the moment
|
||||
*/
|
||||
void createSession(const QString &profileName = QString(), const QString &directory = QString());
|
||||
|
||||
void showManageProfilesDialog(QWidget *parent);
|
||||
|
||||
/**
|
||||
* Shows the dialog used to edit the profile used by the active session. The
|
||||
* dialog will be non-modal and will delete itself when it is closed.
|
||||
*
|
||||
* This is experimental API and not guaranteed to be present in later KDE 4
|
||||
* releases.
|
||||
*
|
||||
* @param parent The parent widget of the new dialog.
|
||||
*/
|
||||
void showEditCurrentProfileDialog(QWidget *parent);
|
||||
/**
|
||||
* Sends a profile change command to the active session. This is equivalent to using
|
||||
* the konsoleprofile tool within the session to change its settings. The @p text string
|
||||
* is a semi-colon separated list of property=value pairs, eg. "colors=Linux Colors"
|
||||
*
|
||||
* See the documentation for konsoleprofile for information on the format of @p text
|
||||
*
|
||||
* This is experimental API and not guaranteed to be present in later KDE 4 releases.
|
||||
*/
|
||||
void changeSessionSettings(const QString &text);
|
||||
|
||||
/**
|
||||
* Connects to an existing pseudo-teletype. See Session::openTeletype().
|
||||
* This must be called before the session is started by startProgram(),
|
||||
* or showShellInDir()
|
||||
*
|
||||
* @param ptyMasterFd The file descriptor of the pseudo-teletype (pty) master
|
||||
* @param runShell When true (default, legacy), runs the teletype in a shell
|
||||
* session environment. When false, the session is not run, so that the
|
||||
* KPtyProcess can be standalone, which may be useful for interactive programs.
|
||||
*/
|
||||
void openTeletype(int ptyMasterFd, bool runShell = true);
|
||||
|
||||
/**
|
||||
* Toggles monitoring for silence in the active session. If silence is detected,
|
||||
* the silenceDetected() signal is emitted.
|
||||
*
|
||||
* @param enabled Whether to enable or disable monitoring for silence.
|
||||
* */
|
||||
void setMonitorSilenceEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
* Toggles monitoring for activity in the active session. If activity is detected,
|
||||
* the activityDetected() signal is emitted.
|
||||
*
|
||||
* @param enabled Whether to enable or disable monitoring for activity.
|
||||
* */
|
||||
void setMonitorActivityEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
* Returns the status of the blur of the current profile.
|
||||
*
|
||||
* @return True if blur is enabled for the current active Konsole color profile.
|
||||
* */
|
||||
bool isBlurEnabled();
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when the key sequence for a shortcut, which is also a valid terminal key sequence,
|
||||
* is pressed while the terminal has focus. By responding to this signal, the
|
||||
* controlling application can choose whether to execute the action associated with
|
||||
* the shortcut or ignore the shortcut and send the key
|
||||
* sequence to the terminal application.
|
||||
*
|
||||
* In the embedded terminal, shortcuts are overridden and sent to the terminal by default.
|
||||
* Set @p override to false to prevent this happening and allow the shortcut to be triggered
|
||||
* normally.
|
||||
*
|
||||
* overrideShortcut() is not called for shortcuts which are not valid terminal key sequences,
|
||||
* eg. shortcuts with two or more modifiers.
|
||||
*
|
||||
* @param event Describes the keys that were pressed.
|
||||
* @param override Set this to false to prevent the terminal display from overriding the shortcut
|
||||
*/
|
||||
void overrideShortcut(QKeyEvent *event, bool &override);
|
||||
|
||||
/**
|
||||
* Emitted when silence has been detected in the active session. Monitoring
|
||||
* for silence has to be enabled first using setMonitorSilenceEnabled().
|
||||
*/
|
||||
void silenceDetected();
|
||||
|
||||
/**
|
||||
* Emitted when activity has been detected in the active session. Monitoring
|
||||
* for activity has to be enabled first using setMonitorActivityEnabled().
|
||||
*/
|
||||
void activityDetected();
|
||||
|
||||
/**
|
||||
* Emitted when the current working directory of the active session has changed.
|
||||
*/
|
||||
void currentDirectoryChanged(const QString &dir);
|
||||
|
||||
protected:
|
||||
/** Reimplemented from KParts::PartBase. */
|
||||
bool openFile() override;
|
||||
bool openUrl(const QUrl &url) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void activeViewChanged(SessionController *controller);
|
||||
void activeViewTitleChanged(ViewProperties *properties);
|
||||
void terminalExited();
|
||||
void newTab();
|
||||
void overrideTerminalShortcut(QKeyEvent *, bool &override);
|
||||
void notificationChanged(Session::Notification notification, bool enabled);
|
||||
|
||||
private:
|
||||
Session *activeSession() const;
|
||||
|
||||
private:
|
||||
ViewManager *_viewManager;
|
||||
SessionController *_pluggedController;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // PART_H
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
This source file is part of Konsole, a terminal emulator.
|
||||
|
||||
SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef POPSTACKONEXIT_H
|
||||
#define POPSTACKONEXIT_H
|
||||
|
||||
#include <QStack>
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
/**
|
||||
* PopStackOnExit is a utility to remove all values from a QStack which are added during
|
||||
* the lifetime of a PopStackOnExit instance.
|
||||
*
|
||||
* When a PopStackOnExit instance is destroyed, elements are removed from the stack
|
||||
* until the stack count is reduced the value when the PopStackOnExit instance was created.
|
||||
*/
|
||||
template<class T>
|
||||
class PopStackOnExit
|
||||
{
|
||||
public:
|
||||
explicit PopStackOnExit(QStack<T> &stack)
|
||||
: _stack(stack)
|
||||
, _count(stack.count())
|
||||
{
|
||||
}
|
||||
|
||||
~PopStackOnExit()
|
||||
{
|
||||
while (_stack.count() > _count) {
|
||||
_stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(PopStackOnExit)
|
||||
|
||||
QStack<T> &_stack;
|
||||
int _count;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2012 Kasper Laudrup <laudrup@stacktrace.dk>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "PrintOptions.h"
|
||||
|
||||
// KDE
|
||||
#include <KConfigGroup>
|
||||
#include <KSharedConfig>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
PrintOptions::PrintOptions(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
|
||||
KConfigGroup configGroup(KSharedConfig::openConfig(), QStringLiteral("PrintOptions"));
|
||||
printerFriendly->setChecked(configGroup.readEntry("PrinterFriendly", true));
|
||||
scaleOutput->setChecked(configGroup.readEntry("ScaleOutput", true));
|
||||
}
|
||||
|
||||
PrintOptions::~PrintOptions() = default;
|
||||
|
||||
void PrintOptions::saveSettings()
|
||||
{
|
||||
KConfigGroup configGroup(KSharedConfig::openConfig(), QStringLiteral("PrintOptions"));
|
||||
configGroup.writeEntry("PrinterFriendly", printerFriendly->isChecked());
|
||||
configGroup.writeEntry("ScaleOutput", scaleOutput->isChecked());
|
||||
}
|
||||
|
||||
#include "moc_PrintOptions.cpp"
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2012 Kasper Laudrup <laudrup@stacktrace.dk>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
#ifndef PRINTOPTIONS_H
|
||||
#define PRINTOPTIONS_H
|
||||
|
||||
#include "ui_PrintOptions.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class PrintOptions : public QWidget, private Ui::PrintOptions
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PrintOptions(QWidget *parent = nullptr);
|
||||
~PrintOptions() override;
|
||||
|
||||
public Q_SLOTS:
|
||||
void saveSettings();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PrintOptions</class>
|
||||
<widget class="QWidget" name="PrintOptions">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>573</width>
|
||||
<height>475</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Output Options</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="OutputOptions">
|
||||
<property name="title">
|
||||
<string>Output Options</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="2" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>223</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="printerFriendly">
|
||||
<property name="text">
|
||||
<string>Printer &friendly mode (black text, no background)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="scaleOutput">
|
||||
<property name="text">
|
||||
<string>&Scale output</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef PROCESSINFO_H
|
||||
#define PROCESSINFO_H
|
||||
|
||||
// Qt
|
||||
#include <QFile>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
#include "konsoleprivate_export.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
/**
|
||||
* Takes a snapshot of the state of a process and provides access to
|
||||
* information such as the process name, parent process,
|
||||
* the foreground process in the controlling terminal,
|
||||
* the arguments with which the process was started.
|
||||
*
|
||||
* To create a new snapshot, construct a new ProcessInfo instance,
|
||||
* using ProcessInfo::newInstance(),
|
||||
* passing the process identifier of the process you are interested in.
|
||||
*
|
||||
* After creating a new instance, call the update() method to take a
|
||||
* snapshot of the current state of the process.
|
||||
*
|
||||
* Before calling any additional methods, check that the process state
|
||||
* was read successfully using the isValid() method.
|
||||
*
|
||||
* Each accessor method which provides information about the process state ( such as pid(),
|
||||
* currentDir(), name() ) takes a pointer to a boolean as an argument. If the information
|
||||
* requested was read successfully then the boolean is set to true, otherwise it is set
|
||||
* to false, in which case the return value from the function should be ignored.
|
||||
* If this boolean is set to false, it may indicate an error reading the process information,
|
||||
* or it may indicate that the information is not available on the current platform.
|
||||
*
|
||||
* eg.
|
||||
*
|
||||
* @code
|
||||
* ProcessInfo* info = ProcessInfo::newInstance(pid);
|
||||
* info->update();
|
||||
*
|
||||
* if ( info->isValid() )
|
||||
* {
|
||||
* bool ok;
|
||||
*
|
||||
* QString name = info->name(&ok);
|
||||
* if ( ok ) qDebug() << "process name - " << name;
|
||||
* int parentPid = info->parentPid(&ok);
|
||||
* if ( ok ) qDebug() << "parent process - " << parentPid;
|
||||
* int foregroundPid = info->foregroundPid(&ok);
|
||||
* if ( ok ) qDebug() << "foreground process - " << foregroundPid;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
class KONSOLEPRIVATE_EXPORT ProcessInfo
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance of a suitable ProcessInfo sub-class for
|
||||
* the current platform which provides information about a given process.
|
||||
*
|
||||
* @param pid The pid of the process to examine
|
||||
* @param sessionPid -1 if examined process is session process,
|
||||
* else will be pid of session process of the tab housing examined process
|
||||
*/
|
||||
static ProcessInfo *newInstance(int pid, int sessionPid = -1);
|
||||
|
||||
virtual ~ProcessInfo()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the information about the process. This must
|
||||
* be called before attempting to use any of the accessor methods.
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* Reads the most recent arguments passed to the process.
|
||||
*/
|
||||
void refreshArguments();
|
||||
|
||||
/** Returns true if the process state was read successfully. */
|
||||
bool isValid() const;
|
||||
/**
|
||||
* Returns the process id.
|
||||
*
|
||||
* @param ok Set to true if the process id was read successfully or false otherwise
|
||||
*/
|
||||
int pid(bool *ok) const;
|
||||
/**
|
||||
* Returns the id of the parent process id was read successfully or false otherwise
|
||||
*
|
||||
* @param ok Set to true if the parent process id
|
||||
*/
|
||||
int parentPid(bool *ok) const;
|
||||
|
||||
/**
|
||||
* Returns the id of the current foreground process
|
||||
*
|
||||
* NOTE: Using the foregroundProcessGroup() method of the Pty
|
||||
* instance associated with the terminal of interest is preferred
|
||||
* over using this method.
|
||||
*
|
||||
* @param ok Set to true if the foreground process id was read successfully or false otherwise
|
||||
*/
|
||||
int foregroundPid(bool *ok) const;
|
||||
|
||||
/* Returns the user id of the process */
|
||||
int userId(bool *ok) const;
|
||||
|
||||
/** Returns the user's name of the process */
|
||||
QString userName() const;
|
||||
|
||||
/** Returns the user's home directory of the process */
|
||||
QString userHomeDir() const;
|
||||
|
||||
/** Returns the local host */
|
||||
static QString localHost();
|
||||
|
||||
/** Returns the name of the current process */
|
||||
QString name(bool *ok) const;
|
||||
|
||||
/**
|
||||
* Returns the command-line arguments which the process
|
||||
* was started with.
|
||||
*
|
||||
* The first argument is the name used to launch the process.
|
||||
*
|
||||
* @param ok Set to true if the arguments were read successfully or false otherwise.
|
||||
*/
|
||||
QVector<QString> arguments(bool *ok) const;
|
||||
|
||||
/**
|
||||
* Returns the current working directory of the process
|
||||
*
|
||||
* @param ok Set to true if the current working directory was read successfully or false otherwise
|
||||
*/
|
||||
QString currentDir(bool *ok) const;
|
||||
|
||||
/**
|
||||
* Returns the current working directory of the process (or its parent)
|
||||
*/
|
||||
QString validCurrentDir() const;
|
||||
|
||||
/** Forces the user home directory to be calculated */
|
||||
void setUserHomeDir();
|
||||
|
||||
/**
|
||||
* This enum describes the errors which can occur when trying to read
|
||||
* a process's information.
|
||||
*/
|
||||
enum Error {
|
||||
/** No error occurred. */
|
||||
NoError,
|
||||
/** The nature of the error is unknown. */
|
||||
UnknownError,
|
||||
/** Konsole does not have permission to obtain the process information. */
|
||||
PermissionsError,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the last error which occurred.
|
||||
*/
|
||||
Error error() const;
|
||||
|
||||
enum Field {
|
||||
PROCESS_ID = 1,
|
||||
PARENT_PID = 2,
|
||||
FOREGROUND_PID = 4,
|
||||
ARGUMENTS = 8,
|
||||
NAME = 16,
|
||||
CURRENT_DIR = 32,
|
||||
UID = 64,
|
||||
};
|
||||
Q_DECLARE_FLAGS(Fields, Field)
|
||||
|
||||
// takes a full directory path and returns a
|
||||
// shortened version suitable for display in
|
||||
// space-constrained UI elements (eg. tabs)
|
||||
QString formatShortDir(const QString &dirPath) const;
|
||||
|
||||
void setUserNameRequired(bool need);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructs a new process instance. You should not call the constructor
|
||||
* of ProcessInfo or its subclasses directly. Instead use the
|
||||
* static ProcessInfo::newInstance() method which will return
|
||||
* a suitable ProcessInfo instance for the current platform.
|
||||
*/
|
||||
explicit ProcessInfo(int pid);
|
||||
|
||||
/**
|
||||
* This is called on construction to read the process state
|
||||
* Subclasses should reimplement this function to provide
|
||||
* platform-specific process state reading functionality.
|
||||
*
|
||||
* When called, readProcessInfo() should attempt to read all
|
||||
* of the necessary state information. If the attempt is successful,
|
||||
* it should set the process id using setPid(), and update
|
||||
* the other relevant information using setParentPid(), setName(),
|
||||
* setArguments() etc.
|
||||
*
|
||||
* Calls to isValid() will return true only if the process id
|
||||
* has been set using setPid()
|
||||
*
|
||||
* @param pid The process id of the process to read
|
||||
*/
|
||||
virtual void readProcessInfo(int pid) = 0;
|
||||
|
||||
/**
|
||||
* Only reads and updates the name of the process
|
||||
* @param pid process ID to use
|
||||
* @return true on success
|
||||
*/
|
||||
virtual bool readProcessName(int pid) = 0;
|
||||
|
||||
/**
|
||||
* Determine the current directory of the process.
|
||||
* @param pid process ID to use
|
||||
* @return true on success
|
||||
*/
|
||||
virtual bool readCurrentDir(int pid) = 0;
|
||||
|
||||
/**
|
||||
* Determine what arguments were passed to the process.
|
||||
* Sets _arguments.
|
||||
* @param pid process ID to use
|
||||
* @return true on success
|
||||
*/
|
||||
virtual bool readArguments(int pid) = 0;
|
||||
|
||||
/* Read the user name */
|
||||
virtual void readUserName(void) = 0;
|
||||
|
||||
/** Sets the process id associated with this ProcessInfo instance */
|
||||
void setPid(int pid);
|
||||
/** Sets the parent process id as returned by parentPid() */
|
||||
void setParentPid(int pid);
|
||||
/** Sets the foreground process id as returned by foregroundPid() */
|
||||
void setForegroundPid(int pid);
|
||||
/** Sets the user id associated with this ProcessInfo instance */
|
||||
void setUserId(int uid);
|
||||
/** Sets the user name of the process as set by readUserName() */
|
||||
void setUserName(const QString &name);
|
||||
/** Sets the name of the process as returned by name() */
|
||||
void setName(const QString &name);
|
||||
/** Sets the current working directory for the process */
|
||||
void setCurrentDir(const QString &dir);
|
||||
|
||||
/** Sets the error */
|
||||
void setError(Error error);
|
||||
|
||||
/** Convenience method. Sets the error based on a QFile error code. */
|
||||
void setFileError(QFile::FileError error);
|
||||
|
||||
/**
|
||||
* Adds a commandline argument for the process, as returned
|
||||
* by arguments()
|
||||
*/
|
||||
void addArgument(const QString &argument);
|
||||
|
||||
/**
|
||||
* clear the commandline arguments for the process, as returned
|
||||
* by arguments()
|
||||
*/
|
||||
void clearArguments();
|
||||
|
||||
bool userNameRequired() const;
|
||||
|
||||
private:
|
||||
Fields _fields;
|
||||
|
||||
int _pid;
|
||||
int _parentPid;
|
||||
int _foregroundPid;
|
||||
int _userId;
|
||||
|
||||
Error _lastError;
|
||||
|
||||
QString _name;
|
||||
QString _userName;
|
||||
QString _userHomeDir;
|
||||
QString _currentDir;
|
||||
|
||||
bool _userNameRequired;
|
||||
|
||||
QVector<QString> _arguments;
|
||||
|
||||
static QStringList commonDirNames();
|
||||
static QStringList _commonDirNames;
|
||||
};
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(ProcessInfo::Fields)
|
||||
|
||||
}
|
||||
|
||||
#endif // PROCESSINFO_H
|
||||
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "Pty.h"
|
||||
|
||||
#include "konsoledebug.h"
|
||||
|
||||
// Qt
|
||||
#include <QStringList>
|
||||
#include <qplatformdefs.h>
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
|
||||
// System
|
||||
#include <csignal>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h> //ioctl() and TIOCSWINSZ
|
||||
#include <termios.h>
|
||||
|
||||
// KDE
|
||||
#include <KPtyDevice>
|
||||
|
||||
using Konsole::Pty;
|
||||
|
||||
Pty::Pty(QObject *aParent)
|
||||
: Pty(-1, aParent)
|
||||
{
|
||||
}
|
||||
|
||||
Pty::Pty(int masterFd, QObject *aParent)
|
||||
: KPtyProcess(masterFd, aParent)
|
||||
{
|
||||
// Must call parent class child process modifier, as it sets file descriptors ...etc
|
||||
auto parentChildProcModifier = KPtyProcess::childProcessModifier();
|
||||
setChildProcessModifier([parentChildProcModifier = std::move(parentChildProcModifier)]() {
|
||||
if (parentChildProcModifier) {
|
||||
parentChildProcModifier();
|
||||
}
|
||||
|
||||
// reset all signal handlers
|
||||
// this ensures that terminal applications respond to
|
||||
// signals generated via key sequences such as Ctrl+C
|
||||
// (which sends SIGINT)
|
||||
struct sigaction action;
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_handler = SIG_DFL;
|
||||
action.sa_flags = 0;
|
||||
for (int signal = 1; signal < NSIG; signal++) {
|
||||
sigaction(signal, &action, nullptr);
|
||||
}
|
||||
});
|
||||
|
||||
_windowColumns = 0;
|
||||
_windowLines = 0;
|
||||
_windowWidth = 0;
|
||||
_windowHeight = 0;
|
||||
_eraseChar = 0;
|
||||
_xonXoff = true;
|
||||
_utf8 = true;
|
||||
|
||||
setEraseChar(_eraseChar);
|
||||
setFlowControlEnabled(_xonXoff);
|
||||
setUtf8Mode(_utf8);
|
||||
|
||||
setWindowSize(_windowColumns, _windowLines, _windowWidth, _windowHeight);
|
||||
|
||||
setUseUtmp(true);
|
||||
setPtyChannels(KPtyProcess::AllChannels);
|
||||
|
||||
connect(pty(), &KPtyDevice::readyRead, this, &Konsole::Pty::dataReceived);
|
||||
}
|
||||
|
||||
Pty::~Pty() = default;
|
||||
|
||||
void Pty::sendData(const QByteArray &data)
|
||||
{
|
||||
if (data.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pty()->write(data) == -1) {
|
||||
qCDebug(KonsoleDebug) << "Could not send input data to terminal process.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Pty::dataReceived()
|
||||
{
|
||||
QByteArray data = pty()->readAll();
|
||||
if (data.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_EMIT receivedData(data.constData(), data.length());
|
||||
}
|
||||
|
||||
void Pty::setWindowSize(int columns, int lines, int width, int height)
|
||||
{
|
||||
_windowColumns = columns;
|
||||
_windowLines = lines;
|
||||
_windowWidth = width;
|
||||
_windowHeight = height;
|
||||
|
||||
if (pty()->masterFd() >= 0) {
|
||||
pty()->setWinSize(_windowLines, _windowColumns, _windowHeight, _windowWidth);
|
||||
}
|
||||
}
|
||||
|
||||
QSize Pty::windowSize() const
|
||||
{
|
||||
return {_windowColumns, _windowLines};
|
||||
}
|
||||
|
||||
QSize Pty::pixelSize() const
|
||||
{
|
||||
return {_windowWidth, _windowHeight};
|
||||
}
|
||||
|
||||
void Pty::setFlowControlEnabled(bool enable)
|
||||
{
|
||||
_xonXoff = enable;
|
||||
|
||||
if (pty()->masterFd() >= 0) {
|
||||
struct ::termios ttmode;
|
||||
pty()->tcGetAttr(&ttmode);
|
||||
if (enable) {
|
||||
ttmode.c_iflag |= (IXOFF | IXON);
|
||||
} else {
|
||||
ttmode.c_iflag &= ~(IXOFF | IXON);
|
||||
}
|
||||
|
||||
if (!pty()->tcSetAttr(&ttmode)) {
|
||||
qCDebug(KonsoleDebug) << "Unable to set terminal attributes.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Pty::flowControlEnabled() const
|
||||
{
|
||||
if (pty()->masterFd() >= 0) {
|
||||
struct ::termios ttmode;
|
||||
pty()->tcGetAttr(&ttmode);
|
||||
// Check only IXON (flow control on output) on master fd, which corresponds to
|
||||
// IXOFF (flow control on input) on slave side.
|
||||
return ((ttmode.c_iflag & IXON) != 0U);
|
||||
} else {
|
||||
qCDebug(KonsoleDebug) << "Unable to get flow control status, terminal not connected.";
|
||||
return _xonXoff;
|
||||
}
|
||||
}
|
||||
|
||||
void Pty::setUtf8Mode(bool enable)
|
||||
{
|
||||
#if defined(IUTF8) // XXX not a reasonable place to check it.
|
||||
_utf8 = enable;
|
||||
|
||||
if (pty()->masterFd() >= 0) {
|
||||
struct ::termios ttmode;
|
||||
pty()->tcGetAttr(&ttmode);
|
||||
if (enable) {
|
||||
ttmode.c_iflag |= IUTF8;
|
||||
} else {
|
||||
ttmode.c_iflag &= ~IUTF8;
|
||||
}
|
||||
|
||||
if (!pty()->tcSetAttr(&ttmode)) {
|
||||
qCDebug(KonsoleDebug) << "Unable to set terminal attributes.";
|
||||
}
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(enable)
|
||||
#endif
|
||||
}
|
||||
|
||||
void Pty::setEraseChar(char eChar)
|
||||
{
|
||||
_eraseChar = eChar;
|
||||
|
||||
if (pty()->masterFd() >= 0) {
|
||||
struct ::termios ttmode;
|
||||
pty()->tcGetAttr(&ttmode);
|
||||
ttmode.c_cc[VERASE] = eChar;
|
||||
|
||||
if (!pty()->tcSetAttr(&ttmode)) {
|
||||
qCDebug(KonsoleDebug) << "Unable to set terminal attributes.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char Pty::eraseChar() const
|
||||
{
|
||||
if (pty()->masterFd() >= 0) {
|
||||
struct ::termios ttyAttributes;
|
||||
pty()->tcGetAttr(&ttyAttributes);
|
||||
return ttyAttributes.c_cc[VERASE];
|
||||
} else {
|
||||
qCDebug(KonsoleDebug) << "Unable to get erase char attribute, terminal not connected.";
|
||||
return _eraseChar;
|
||||
}
|
||||
}
|
||||
|
||||
void Pty::setInitialWorkingDirectory(const QString &dir)
|
||||
{
|
||||
QString pwd = dir;
|
||||
|
||||
// remove trailing slash in the path when appropriate
|
||||
// example: /usr/share/icons/ ==> /usr/share/icons
|
||||
if (pwd.length() > 1 && pwd.endsWith(QLatin1Char('/'))) {
|
||||
pwd.chop(1);
|
||||
}
|
||||
|
||||
setWorkingDirectory(pwd);
|
||||
|
||||
// setting PWD to "." will cause problem for bash & zsh
|
||||
if (pwd != QLatin1String(".")) {
|
||||
setEnv(QStringLiteral("PWD"), pwd);
|
||||
}
|
||||
}
|
||||
|
||||
void Pty::addEnvironmentVariables(const QStringList &environmentVariables)
|
||||
{
|
||||
bool isTermEnvAdded = false;
|
||||
|
||||
for (const QString &pair : environmentVariables) {
|
||||
// split on the first '=' character
|
||||
const int separator = pair.indexOf(QLatin1Char('='));
|
||||
|
||||
if (separator >= 0) {
|
||||
QString variable = pair.left(separator);
|
||||
QString value = pair.mid(separator + 1);
|
||||
|
||||
setEnv(variable, value);
|
||||
|
||||
if (variable == QLatin1String("TERM")) {
|
||||
isTermEnvAdded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// extra safeguard to make sure $TERM is always set
|
||||
if (!isTermEnvAdded) {
|
||||
setEnv(QStringLiteral("TERM"), QStringLiteral("xterm-256color"));
|
||||
}
|
||||
}
|
||||
|
||||
int Pty::start(const QString &programName, const QStringList &programArguments, const QStringList &environmentList)
|
||||
{
|
||||
clearProgram();
|
||||
|
||||
setProgram(programName, programArguments);
|
||||
|
||||
addEnvironmentVariables(environmentList);
|
||||
|
||||
// unless the LANGUAGE environment variable has been set explicitly
|
||||
// set it to a null string
|
||||
// this fixes the problem where KCatalog sets the LANGUAGE environment
|
||||
// variable during the application's startup to something which
|
||||
// differs from LANG,LC_* etc. and causes programs run from
|
||||
// the terminal to display messages in the wrong language
|
||||
//
|
||||
// this can happen if LANG contains a language which KDE
|
||||
// does not have a translation for
|
||||
//
|
||||
// BR:149300
|
||||
setEnv(QStringLiteral("LANGUAGE"), QString(), false /* do not overwrite existing value if any */);
|
||||
|
||||
KProcess::start();
|
||||
|
||||
if (waitForStarted()) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void Pty::setWriteable(bool writeable)
|
||||
{
|
||||
QT_STATBUF sbuf;
|
||||
if (QT_STAT(pty()->ttyName(), &sbuf) == 0) {
|
||||
if (writeable) {
|
||||
if (::chmod(pty()->ttyName(), sbuf.st_mode | S_IWGRP) < 0) {
|
||||
qCDebug(KonsoleDebug) << "Could not set writeable on " << pty()->ttyName();
|
||||
}
|
||||
} else {
|
||||
if (::chmod(pty()->ttyName(), sbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
|
||||
qCDebug(KonsoleDebug) << "Could not unset writeable on " << pty()->ttyName();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qCDebug(KonsoleDebug) << "Could not stat " << pty()->ttyName();
|
||||
}
|
||||
}
|
||||
|
||||
void Pty::closePty()
|
||||
{
|
||||
pty()->close();
|
||||
}
|
||||
|
||||
int Pty::foregroundProcessGroup() const
|
||||
{
|
||||
const int master_fd = pty()->masterFd();
|
||||
|
||||
if (master_fd >= 0) {
|
||||
int foregroundPid = tcgetpgrp(master_fd);
|
||||
|
||||
if (foregroundPid != -1) {
|
||||
return foregroundPid;
|
||||
} else {
|
||||
qCWarning(KonsoleDebug, "Failed to get foreground process group id for %d: %s", master_fd, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
qWarning(KonsoleDebug, "foregroundProcessGroup master_fd < 0");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // Windows backend
|
||||
|
||||
#include "ptyqt/conptyprocess.h"
|
||||
|
||||
using Konsole::Pty;
|
||||
|
||||
Pty::Pty(QObject *aParent)
|
||||
: Pty(-1, aParent)
|
||||
{
|
||||
}
|
||||
|
||||
Pty::Pty(int masterFd, QObject *aParent)
|
||||
: QObject(aParent)
|
||||
{
|
||||
Q_UNUSED(masterFd)
|
||||
|
||||
m_proc = std::make_unique<ConPtyProcess>();
|
||||
if (!m_proc->isAvailable()) {
|
||||
m_proc.reset();
|
||||
}
|
||||
|
||||
_windowColumns = 0;
|
||||
_windowLines = 0;
|
||||
_windowWidth = 0;
|
||||
_windowHeight = 0;
|
||||
_eraseChar = 0;
|
||||
_xonXoff = true;
|
||||
_utf8 = true;
|
||||
|
||||
setEraseChar(_eraseChar);
|
||||
}
|
||||
|
||||
Pty::~Pty() = default;
|
||||
|
||||
void Pty::sendData(const QByteArray &data)
|
||||
{
|
||||
if (m_proc) {
|
||||
m_proc->write(data.constData(), data.length());
|
||||
}
|
||||
}
|
||||
|
||||
void Pty::dataReceived()
|
||||
{
|
||||
if (m_proc) {
|
||||
auto data = m_proc->readAll();
|
||||
Q_EMIT receivedData(data.constData(), data.length());
|
||||
}
|
||||
}
|
||||
|
||||
void Pty::setWindowSize(int columns, int lines, int, int)
|
||||
{
|
||||
if (m_proc && isRunning())
|
||||
m_proc->resize(columns, lines);
|
||||
}
|
||||
|
||||
QSize Pty::windowSize() const
|
||||
{
|
||||
if (!m_proc) {
|
||||
return {};
|
||||
}
|
||||
auto s = m_proc->size();
|
||||
return QSize(s.first, s.second);
|
||||
}
|
||||
|
||||
QSize Pty::pixelSize() const
|
||||
{
|
||||
return QSize();
|
||||
}
|
||||
|
||||
void Pty::setFlowControlEnabled(bool enable)
|
||||
{
|
||||
_xonXoff = enable;
|
||||
}
|
||||
|
||||
bool Pty::flowControlEnabled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Pty::setUtf8Mode(bool)
|
||||
{
|
||||
}
|
||||
|
||||
void Pty::setEraseChar(char eChar)
|
||||
{
|
||||
_eraseChar = eChar;
|
||||
}
|
||||
|
||||
char Pty::eraseChar() const
|
||||
{
|
||||
return _eraseChar;
|
||||
}
|
||||
|
||||
void Pty::setInitialWorkingDirectory(const QString & /*dir*/)
|
||||
{
|
||||
}
|
||||
|
||||
void Pty::addEnvironmentVariables(const QStringList & /*environmentVariables*/)
|
||||
{
|
||||
}
|
||||
|
||||
int Pty::start(const QString &program, const QStringList &arguments, const QString &workingDir, const QStringList &environment, int cols, int lines)
|
||||
{
|
||||
if (!m_proc || !m_proc->isAvailable()) {
|
||||
return -1;
|
||||
}
|
||||
bool res = m_proc->startProcess(program, arguments, workingDir, environment, cols, lines);
|
||||
if (!res) {
|
||||
return -1;
|
||||
} else {
|
||||
auto n = m_proc->notifier();
|
||||
connect(n, &QIODevice::readyRead, this, &Pty::dataReceived);
|
||||
connect(m_proc.get(), &IPtyProcess::exited, this, [this] {
|
||||
Q_EMIT finished(exitCode(), QProcess::NormalExit);
|
||||
});
|
||||
connect(n, &QIODevice::aboutToClose, this, [this] {
|
||||
Q_EMIT finished(exitCode(), QProcess::NormalExit);
|
||||
});
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Pty::setWriteable(bool)
|
||||
{
|
||||
}
|
||||
|
||||
void Pty::closePty()
|
||||
{
|
||||
if (m_proc) {
|
||||
m_proc->kill();
|
||||
}
|
||||
}
|
||||
|
||||
int Pty::foregroundProcessGroup() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#include "moc_Pty.cpp"
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef PTY_H
|
||||
#define PTY_H
|
||||
|
||||
// Qt
|
||||
#include <QProcess>
|
||||
#include <QSize>
|
||||
|
||||
// Konsole
|
||||
#include "konsoleprivate_export.h"
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
// KDE
|
||||
#include <KPtyProcess>
|
||||
#else
|
||||
#include <QObject>
|
||||
|
||||
#include "ptyqt/iptyprocess.h"
|
||||
#endif
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
/**
|
||||
* The Pty class is used to start the terminal process,
|
||||
* send data to it, receive data from it and manipulate
|
||||
* various properties of the pseudo-teletype interface
|
||||
* used to communicate with the process.
|
||||
*
|
||||
* To use this class, construct an instance and connect
|
||||
* to the sendData slot and receivedData signal to
|
||||
* send data to or receive data from the process.
|
||||
*
|
||||
* To start the terminal process, call the start() method
|
||||
* with the program name and appropriate arguments.
|
||||
*/
|
||||
#ifdef Q_OS_WIN
|
||||
#define ParentClass QObject
|
||||
#else
|
||||
#define ParentClass KPtyProcess
|
||||
#endif
|
||||
|
||||
class KONSOLEPRIVATE_EXPORT Pty : public ParentClass
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a new Pty.
|
||||
*
|
||||
* Connect to the sendData() slot and receivedData() signal to prepare
|
||||
* for sending and receiving data from the terminal process.
|
||||
*
|
||||
* To start the terminal process, call the run() method with the
|
||||
* name of the program to start and appropriate arguments.
|
||||
*/
|
||||
explicit Pty(QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Construct a process using an open pty master.
|
||||
* See KPtyProcess::KPtyProcess()
|
||||
*/
|
||||
explicit Pty(int ptyMasterFd, QObject *parent = nullptr);
|
||||
|
||||
~Pty() override;
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
/**
|
||||
* Starts the terminal process.
|
||||
*
|
||||
* Returns 0 if the process was started successfully or non-zero
|
||||
* otherwise.
|
||||
*
|
||||
* @param program Path to the program to start
|
||||
* @param arguments Arguments to pass to the program being started
|
||||
* @param environment A list of key=value pairs which will be added
|
||||
* to the environment for the new process. At the very least this
|
||||
* should include an assignment for the TERM environment variable.
|
||||
*/
|
||||
int start(const QString &program, const QStringList &arguments, const QStringList &environment);
|
||||
#else
|
||||
/**
|
||||
* Starts the terminal process.
|
||||
*
|
||||
* Returns 0 if the process was started successfully or non-zero
|
||||
* otherwise.
|
||||
*
|
||||
* @param program Path to the program to start
|
||||
* @param arguments Arguments to pass to the program being started
|
||||
* @param workingDir initial working directory
|
||||
* @param environment A list of key=value pairs which will be added
|
||||
* to the environment for the new process. At the very least this
|
||||
* should include an assignment for the TERM environment variable.
|
||||
*/
|
||||
int start(const QString &program, const QStringList &arguments, const QString &workingDir, const QStringList &environment, int cols, int lines);
|
||||
#endif
|
||||
|
||||
/** Control whether the pty device is writeable by group members. */
|
||||
void setWriteable(bool writeable);
|
||||
|
||||
/**
|
||||
* Enables or disables Xon/Xoff flow control. The flow control setting
|
||||
* may be changed later by a terminal application, so flowControlEnabled()
|
||||
* may not equal the value of @p on in the previous call to setFlowControlEnabled()
|
||||
*/
|
||||
void setFlowControlEnabled(bool on);
|
||||
|
||||
/** Queries the terminal state and returns true if Xon/Xoff flow control is enabled. */
|
||||
bool flowControlEnabled() const;
|
||||
|
||||
/**
|
||||
* Sets the size of the window (in columns and lines of characters,
|
||||
* and width and height in pixels) used by this teletype.
|
||||
*/
|
||||
void setWindowSize(int columns, int lines, int width, int height);
|
||||
|
||||
/** Returns the size of the window used by this teletype in characters. See setWindowSize() */
|
||||
QSize windowSize() const;
|
||||
|
||||
/** Returns the size of the window used by this teletype in pixels. See setWindowSize() */
|
||||
QSize pixelSize() const;
|
||||
|
||||
/**
|
||||
* Sets the special character for erasing previous not-yet-erased character.
|
||||
* See termios(3) for detailed description.
|
||||
*/
|
||||
void setEraseChar(char eraseChar);
|
||||
|
||||
/** */
|
||||
char eraseChar() const;
|
||||
|
||||
/**
|
||||
* Sets the initial working directory.
|
||||
*/
|
||||
void setInitialWorkingDirectory(const QString &dir);
|
||||
|
||||
/**
|
||||
* Returns the process id of the teletype's current foreground
|
||||
* process. This is the process which is currently reading
|
||||
* input sent to the terminal via. sendData()
|
||||
*
|
||||
* If there is a problem reading the foreground process group,
|
||||
* 0 will be returned.
|
||||
*/
|
||||
int foregroundProcessGroup() const;
|
||||
|
||||
/**
|
||||
* Close the underlying pty master/slave pair.
|
||||
*/
|
||||
void closePty();
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
int processId() const
|
||||
{
|
||||
if (m_proc && m_proc->isAvailable()) {
|
||||
return m_proc->pid();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isRunning() const
|
||||
{
|
||||
return processId() > 0;
|
||||
}
|
||||
|
||||
QString errorString() const
|
||||
{
|
||||
if (m_proc) {
|
||||
return m_proc->lastError();
|
||||
}
|
||||
return QStringLiteral("Conhost failed to start");
|
||||
}
|
||||
|
||||
bool kill()
|
||||
{
|
||||
if (m_proc) {
|
||||
return m_proc->kill();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int exitCode() const
|
||||
{
|
||||
if (m_proc) {
|
||||
return m_proc->exitCode();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Q_SIGNAL void finished(int exitCode, QProcess::ExitStatus);
|
||||
#endif
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Put the pty into UTF-8 mode on systems which support it.
|
||||
*/
|
||||
void setUtf8Mode(bool on);
|
||||
|
||||
/**
|
||||
* Sends data to the process currently controlling the
|
||||
* teletype ( whose id is returned by foregroundProcessGroup() )
|
||||
*
|
||||
* @param data the data to send.
|
||||
*/
|
||||
void sendData(const QByteArray &data);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when a new block of data is received from
|
||||
* the teletype.
|
||||
*
|
||||
* @param buffer Pointer to the data received.
|
||||
* @param length Length of @p buffer
|
||||
*/
|
||||
void receivedData(const char *buffer, int length);
|
||||
|
||||
private Q_SLOTS:
|
||||
// called when data is received from the terminal process
|
||||
void dataReceived();
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
// takes a list of key=value pairs and adds them
|
||||
// to the environment for the process
|
||||
void addEnvironmentVariables(const QStringList &environment);
|
||||
|
||||
int _windowColumns;
|
||||
int _windowLines;
|
||||
int _windowWidth;
|
||||
int _windowHeight;
|
||||
char _eraseChar;
|
||||
bool _xonXoff;
|
||||
bool _utf8;
|
||||
#ifdef Q_OS_WIN
|
||||
std::unique_ptr<IPtyProcess> m_proc;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#undef ParentClass
|
||||
|
||||
#endif // PTY_H
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2010 Kurt Hindenburg <kurt.hindenburg@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "RenameTabDialog.h"
|
||||
|
||||
// Konsole
|
||||
#include "Shortcut_p.h"
|
||||
#include "ui_RenameTabDialog.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
using Konsole::RenameTabDialog;
|
||||
|
||||
RenameTabDialog::RenameTabDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, _ui(nullptr)
|
||||
{
|
||||
setWindowTitle(i18n("Tab Properties"));
|
||||
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
auto mainWidget = new QWidget(this);
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
setLayout(mainLayout);
|
||||
mainLayout->addWidget(mainWidget);
|
||||
QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
|
||||
okButton->setAutoDefault(true);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &RenameTabDialog::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &RenameTabDialog::reject);
|
||||
mainLayout->addWidget(buttonBox);
|
||||
|
||||
setWindowModality(Qt::WindowModal);
|
||||
|
||||
_ui = new Ui::RenameTabDialog();
|
||||
_ui->setupUi(mainWidget);
|
||||
}
|
||||
|
||||
RenameTabDialog::~RenameTabDialog()
|
||||
{
|
||||
delete _ui;
|
||||
}
|
||||
|
||||
void RenameTabDialog::focusTabTitleText()
|
||||
{
|
||||
_ui->renameTabWidget->focusTabTitleText();
|
||||
}
|
||||
|
||||
void RenameTabDialog::focusRemoteTabTitleText()
|
||||
{
|
||||
_ui->renameTabWidget->focusRemoteTabTitleText();
|
||||
}
|
||||
|
||||
void RenameTabDialog::setTabTitleText(const QString &text)
|
||||
{
|
||||
_ui->renameTabWidget->setTabTitleText(text);
|
||||
}
|
||||
|
||||
void RenameTabDialog::setRemoteTabTitleText(const QString &text)
|
||||
{
|
||||
_ui->renameTabWidget->setRemoteTabTitleText(text);
|
||||
}
|
||||
|
||||
void RenameTabDialog::setColor(const QColor &color)
|
||||
{
|
||||
_ui->renameTabWidget->setColor(color);
|
||||
}
|
||||
|
||||
QString RenameTabDialog::tabTitleText() const
|
||||
{
|
||||
return _ui->renameTabWidget->tabTitleText();
|
||||
}
|
||||
|
||||
QString RenameTabDialog::remoteTabTitleText() const
|
||||
{
|
||||
return _ui->renameTabWidget->remoteTabTitleText();
|
||||
}
|
||||
|
||||
QColor RenameTabDialog::color() const
|
||||
{
|
||||
return _ui->renameTabWidget->color();
|
||||
}
|
||||
|
||||
#include "moc_RenameTabDialog.cpp"
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2010 Kurt Hindenburg <kurt.hindenburg@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef RENAMETABDIALOG_H
|
||||
#define RENAMETABDIALOG_H
|
||||
|
||||
// KDE
|
||||
#include "konsoleprivate_export.h"
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class RenameTabDialog;
|
||||
}
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class KONSOLEPRIVATE_EXPORT RenameTabDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RenameTabDialog(QWidget *parent = nullptr);
|
||||
~RenameTabDialog() override;
|
||||
|
||||
QString tabTitleText() const;
|
||||
QString remoteTabTitleText() const;
|
||||
QColor color() const;
|
||||
void setTabTitleText(const QString &);
|
||||
void setRemoteTabTitleText(const QString &);
|
||||
void setColor(const QColor &);
|
||||
|
||||
void focusTabTitleText();
|
||||
void focusRemoteTabTitleText();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(RenameTabDialog)
|
||||
|
||||
Ui::RenameTabDialog *_ui;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RenameTabDialog</class>
|
||||
<widget class="QWidget" name="RenameTabDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>325</width>
|
||||
<height>110</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="Konsole::RenameTabWidget" name="renameTabWidget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Konsole::RenameTabWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/RenameTabWidget.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "SSHProcessInfo.h"
|
||||
|
||||
// Unix
|
||||
#ifndef Q_OS_WIN
|
||||
#include <arpa/inet.h>
|
||||
#include <cerrno>
|
||||
#include <netinet/in.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
// Qt
|
||||
#include <QDebug>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
SSHProcessInfo::SSHProcessInfo(const ProcessInfo &process)
|
||||
: _process(process)
|
||||
, _user(QString())
|
||||
, _host(QString())
|
||||
, _port(QString())
|
||||
, _command(QString())
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
// check that this is a SSH process
|
||||
const QString &name = _process.name(&ok);
|
||||
|
||||
if (!ok || name != QLatin1String("ssh")) {
|
||||
if (!ok) {
|
||||
qWarning() << "Could not read process info";
|
||||
} else {
|
||||
qWarning() << "Process is not a SSH process";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// read arguments
|
||||
const QVector<QString> &args = _process.arguments(&ok);
|
||||
|
||||
// SSH options
|
||||
// these are taken from the SSH manual ( accessed via 'man ssh' )
|
||||
|
||||
// options which take no arguments
|
||||
static const QString noArgumentOptions(QStringLiteral("1246AaCfgKkMNnqsTtVvXxYy"));
|
||||
// options which take one argument
|
||||
static const QString singleArgumentOptions(QStringLiteral("bcDeFIiJLlmOopRSWw"));
|
||||
|
||||
if (ok) {
|
||||
// find the username, host and command arguments
|
||||
//
|
||||
// the username/host is assumed to be the first argument
|
||||
// which is not an option
|
||||
// ( ie. does not start with a dash '-' character )
|
||||
// or an argument to a previous option.
|
||||
//
|
||||
// the command, if specified, is assumed to be the argument following
|
||||
// the username and host
|
||||
//
|
||||
// note that we skip the argument at index 0 because that is the
|
||||
// program name ( expected to be 'ssh' in this case )
|
||||
for (int i = 1; i < args.count(); i++) {
|
||||
// If this one is an option ...
|
||||
// Most options together with its argument will be skipped.
|
||||
if (args[i].startsWith(QLatin1Char('-'))) {
|
||||
const QChar optionChar = (args[i].length() > 1) ? args[i][1] : QLatin1Char('\0');
|
||||
// for example: -p2222 or -p 2222 ?
|
||||
const bool optionArgumentCombined = args[i].length() > 2;
|
||||
|
||||
if (noArgumentOptions.contains(optionChar)) {
|
||||
continue;
|
||||
} else if (singleArgumentOptions.contains(optionChar)) {
|
||||
QString argument;
|
||||
if (optionArgumentCombined) {
|
||||
argument = args[i].mid(2);
|
||||
} else {
|
||||
// Verify correct # arguments are given
|
||||
if ((i + 1) < args.count()) {
|
||||
argument = args[i + 1];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// support using `-l user` to specify username.
|
||||
if (optionChar == QLatin1Char('l')) {
|
||||
_user = argument;
|
||||
}
|
||||
// support using `-p port` to specify port.
|
||||
else if (optionChar == QLatin1Char('p')) {
|
||||
_port = argument;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// check whether the host has been found yet
|
||||
// if not, this must be the username/host argument
|
||||
if (_host.isEmpty()) {
|
||||
// check to see if only a hostname is specified, or whether
|
||||
// both a username and host are specified ( in which case they
|
||||
// are separated by an '@' character: username@host )
|
||||
|
||||
const int separatorPosition = args[i].indexOf(QLatin1Char('@'));
|
||||
if (separatorPosition != -1) {
|
||||
// username and host specified
|
||||
_user = args[i].left(separatorPosition);
|
||||
_host = args[i].mid(separatorPosition + 1);
|
||||
} else {
|
||||
// just the host specified
|
||||
_host = args[i];
|
||||
}
|
||||
} else {
|
||||
// host has already been found, this must be part of the
|
||||
// command arguments.
|
||||
// Note this is not 100% correct. If any of the above
|
||||
// noArgumentOptions or singleArgumentOptions are found, this
|
||||
// will not be correct (example ssh server top -i 50)
|
||||
// Suggest putting ssh command in quotes
|
||||
if (_command.isEmpty()) {
|
||||
_command = args[i];
|
||||
} else {
|
||||
_command = _command + QLatin1Char(' ') + args[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Could not read arguments";
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QString SSHProcessInfo::userName() const
|
||||
{
|
||||
return _user;
|
||||
}
|
||||
|
||||
QString SSHProcessInfo::host() const
|
||||
{
|
||||
return _host;
|
||||
}
|
||||
|
||||
QString SSHProcessInfo::port() const
|
||||
{
|
||||
return _port;
|
||||
}
|
||||
|
||||
QString SSHProcessInfo::command() const
|
||||
{
|
||||
return _command;
|
||||
}
|
||||
|
||||
QString SSHProcessInfo::format(const QString &input) const
|
||||
{
|
||||
QString output(input);
|
||||
|
||||
// search for and replace known markers
|
||||
output.replace(QLatin1String("%u"), _user);
|
||||
|
||||
// provide 'user@' if user is defined -- this makes nicer
|
||||
// remote tabs possible: "%U%h %c" => User@Host Command
|
||||
// => Host Command
|
||||
// Depending on whether -l was passed to ssh (which is mostly not the
|
||||
// case due to ~/.ssh/config).
|
||||
if (_user.isEmpty()) {
|
||||
output.remove(QLatin1String("%U"));
|
||||
} else {
|
||||
output.replace(QLatin1String("%U"), _user + QLatin1Char('@'));
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
// TODO
|
||||
#else
|
||||
// test whether host is an ip address
|
||||
// in which case 'short host' and 'full host'
|
||||
// markers in the input string are replaced with
|
||||
// the full address
|
||||
struct in_addr address;
|
||||
const bool isIpAddress = inet_aton(_host.toLocal8Bit().constData(), &address) != 0;
|
||||
if (isIpAddress) {
|
||||
output.replace(QLatin1String("%h"), _host);
|
||||
} else {
|
||||
output.replace(QLatin1String("%h"), _host.left(_host.indexOf(QLatin1Char('.'))));
|
||||
}
|
||||
|
||||
QString fullHost = _host;
|
||||
if (!_port.isEmpty() && _port != QLatin1String("22")) {
|
||||
fullHost.append(QLatin1Char(':'));
|
||||
fullHost.append(_port);
|
||||
}
|
||||
output.replace(QLatin1String("%H"), fullHost);
|
||||
output.replace(QLatin1String("%c"), _command);
|
||||
#endif
|
||||
return output;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SSHPROCESSINFO_H
|
||||
#define SSHPROCESSINFO_H
|
||||
|
||||
#include "ProcessInfo.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
/**
|
||||
* Lightweight class which provides additional information about SSH processes.
|
||||
*/
|
||||
class KONSOLEPRIVATE_EXPORT SSHProcessInfo
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructs a new SSHProcessInfo instance which provides additional
|
||||
* information about the specified SSH process.
|
||||
*
|
||||
* @param process A ProcessInfo instance for a SSH process.
|
||||
*/
|
||||
explicit SSHProcessInfo(const ProcessInfo &process);
|
||||
|
||||
/**
|
||||
* Returns the user name which the user initially logged into on
|
||||
* the remote computer.
|
||||
*/
|
||||
QString userName() const;
|
||||
|
||||
/**
|
||||
* Returns the host which the user has connected to.
|
||||
*/
|
||||
QString host() const;
|
||||
|
||||
/**
|
||||
* Returns the port on host which the user has connected to.
|
||||
*/
|
||||
QString port() const;
|
||||
|
||||
/**
|
||||
* Returns the command which the user specified to execute on the
|
||||
* remote computer when starting the SSH process.
|
||||
*/
|
||||
QString command() const;
|
||||
|
||||
/**
|
||||
* Operates in the same way as ProcessInfo::format(), except
|
||||
* that the set of markers understood is different:
|
||||
*
|
||||
* %u - Replaced with user name which the user initially logged
|
||||
* into on the remote computer.
|
||||
* %h - Replaced with the first part of the host name which
|
||||
* is connected to.
|
||||
* %H - Replaced with the full host name of the computer which
|
||||
* is connected to.
|
||||
* %c - Replaced with the command which the user specified
|
||||
* to execute when starting the SSH process.
|
||||
*/
|
||||
QString format(const QString &input) const;
|
||||
|
||||
private:
|
||||
const ProcessInfo &_process;
|
||||
QString _user;
|
||||
QString _host;
|
||||
QString _port;
|
||||
QString _command;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
|
||||
SPDX-FileCopyrightText: 2009 Thomas Dreibholz <dreibh@iem.uni-due.de>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "SaveHistoryTask.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFileDialog>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <KConfig>
|
||||
#include <KConfigGroup>
|
||||
#include <KIO/TransferJob>
|
||||
#include <KLocalizedString>
|
||||
#include <KMessageBox>
|
||||
#include <KSharedConfig>
|
||||
|
||||
#include "Emulation.h"
|
||||
#include "session/SessionManager.h"
|
||||
|
||||
#include "../decoders/HTMLDecoder.h"
|
||||
#include "../decoders/PlainTextDecoder.h"
|
||||
#include "colorscheme/ColorScheme.h"
|
||||
#include "colorscheme/ColorSchemeManager.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
QString SaveHistoryTask::_saveDialogRecentURL;
|
||||
|
||||
SaveHistoryTask::SaveHistoryTask(QObject *parent)
|
||||
: SessionTask(parent)
|
||||
{
|
||||
}
|
||||
|
||||
SaveHistoryTask::~SaveHistoryTask() = default;
|
||||
|
||||
void SaveHistoryTask::execute()
|
||||
{
|
||||
// TODO - think about the UI when saving multiple history sessions, if there are more than two or
|
||||
// three then providing a URL for each one will be tedious
|
||||
|
||||
// TODO - show a warning ( preferably passive ) if saving the history output fails
|
||||
QFileDialog *dialog = new QFileDialog(QApplication::activeWindow());
|
||||
dialog->setAcceptMode(QFileDialog::AcceptSave);
|
||||
|
||||
QStringList mimeTypes{QStringLiteral("text/plain"), QStringLiteral("text/html")};
|
||||
dialog->setMimeTypeFilters(mimeTypes);
|
||||
|
||||
KSharedConfigPtr konsoleConfig = KSharedConfig::openConfig();
|
||||
auto group = konsoleConfig->group(QStringLiteral("SaveHistory Settings"));
|
||||
|
||||
if (_saveDialogRecentURL.isEmpty()) {
|
||||
const auto list = group.readPathEntry("Recent URLs", QStringList());
|
||||
if (list.isEmpty()) {
|
||||
dialog->setDirectory(QDir::homePath());
|
||||
} else {
|
||||
dialog->setDirectoryUrl(QUrl(list.at(0)));
|
||||
}
|
||||
} else {
|
||||
dialog->setDirectoryUrl(QUrl(_saveDialogRecentURL));
|
||||
}
|
||||
|
||||
// iterate over each session in the task and display a dialog to allow the user to choose where
|
||||
// to save that session's history.
|
||||
// then start a KIO job to transfer the data from the history to the chosen URL
|
||||
const QList<QPointer<Session>> sessionsList = sessions();
|
||||
for (const auto &session : sessionsList) {
|
||||
dialog->setWindowTitle(i18n("Save Output From %1", session->title(Session::NameRole)));
|
||||
|
||||
int result = dialog->exec();
|
||||
|
||||
if (result != QDialog::Accepted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QUrl url = (dialog->selectedUrls()).at(0);
|
||||
|
||||
if (!url.isValid()) {
|
||||
// UI: Can we make this friendlier?
|
||||
KMessageBox::error(nullptr, i18n("%1 is an invalid URL, the output could not be saved.", url.url()));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Save selected URL for next time
|
||||
_saveDialogRecentURL = url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toString();
|
||||
group.writePathEntry("Recent URLs", _saveDialogRecentURL);
|
||||
|
||||
KIO::TransferJob *job = KIO::put(url,
|
||||
-1, // no special permissions
|
||||
// overwrite existing files
|
||||
// do not resume an existing transfer
|
||||
// show progress information only for remote
|
||||
// URLs
|
||||
KIO::Overwrite | (url.isLocalFile() ? KIO::HideProgressInfo : KIO::DefaultFlags)
|
||||
// a better solution would be to show progress
|
||||
// information after a certain period of time
|
||||
// instead, since the overall speed of transfer
|
||||
// depends on factors other than just the protocol
|
||||
// used
|
||||
);
|
||||
|
||||
SaveJob jobInfo;
|
||||
jobInfo.session = session;
|
||||
jobInfo.lastLineFetched = -1; // when each request for data comes in from the KIO subsystem
|
||||
// lastLineFetched is used to keep track of how much of the history
|
||||
// has already been sent, and where the next request should continue
|
||||
// from.
|
||||
// this is set to -1 to indicate the job has just been started
|
||||
|
||||
if (((dialog->selectedNameFilter()).contains(QLatin1String("html"), Qt::CaseInsensitive))
|
||||
|| ((dialog->selectedFiles()).at(0).endsWith(QLatin1String("html"), Qt::CaseInsensitive))) {
|
||||
Profile::Ptr profile = SessionManager::instance()->sessionProfile(session);
|
||||
const auto schemeName = profile->colorScheme();
|
||||
const auto scheme = ColorSchemeManager::instance()->findColorScheme(schemeName);
|
||||
QColor colorTable[TABLE_COLORS];
|
||||
if (scheme) {
|
||||
scheme->getColorTable(colorTable);
|
||||
} else {
|
||||
std::copy_n(ColorScheme::defaultTable, TABLE_COLORS, colorTable);
|
||||
}
|
||||
|
||||
jobInfo.decoder = new HTMLDecoder(colorTable, profile->font());
|
||||
} else {
|
||||
jobInfo.decoder = new PlainTextDecoder();
|
||||
}
|
||||
|
||||
_jobSession.insert(job, jobInfo);
|
||||
|
||||
connect(job, &KIO::TransferJob::dataReq, this, &Konsole::SaveHistoryTask::jobDataRequested);
|
||||
connect(job, &KIO::TransferJob::result, this, &Konsole::SaveHistoryTask::jobResult);
|
||||
}
|
||||
|
||||
dialog->deleteLater();
|
||||
}
|
||||
|
||||
void SaveHistoryTask::jobDataRequested(KIO::Job *job, QByteArray &data)
|
||||
{
|
||||
// TODO - Report progress information for the job
|
||||
|
||||
// PERFORMANCE: Do some tests and tweak this value to get faster saving
|
||||
const int LINES_PER_REQUEST = 500;
|
||||
|
||||
SaveJob &info = _jobSession[job];
|
||||
|
||||
// transfer LINES_PER_REQUEST lines from the session's history
|
||||
// to the save location
|
||||
if (!info.session.isNull()) {
|
||||
// note: when retrieving lines from the emulation,
|
||||
// the first line is at index 0.
|
||||
|
||||
int sessionLines = info.session->emulation()->lineCount();
|
||||
|
||||
if (sessionLines - 1 == info.lastLineFetched) {
|
||||
return; // if there is no more data to transfer then stop the job
|
||||
}
|
||||
|
||||
int copyUpToLine = qMin(info.lastLineFetched + LINES_PER_REQUEST, sessionLines - 1);
|
||||
|
||||
QTextStream stream(&data, QIODevice::ReadWrite);
|
||||
info.decoder->begin(&stream);
|
||||
info.session->emulation()->writeToStream(info.decoder, info.lastLineFetched + 1, copyUpToLine);
|
||||
info.decoder->end();
|
||||
|
||||
info.lastLineFetched = copyUpToLine;
|
||||
}
|
||||
}
|
||||
void SaveHistoryTask::jobResult(KJob *job)
|
||||
{
|
||||
if (job->error() != 0) {
|
||||
KMessageBox::error(nullptr, i18n("A problem occurred when saving the output.\n%1", job->errorString()));
|
||||
}
|
||||
|
||||
TerminalCharacterDecoder *decoder = _jobSession[job].decoder;
|
||||
|
||||
_jobSession.remove(job);
|
||||
|
||||
delete decoder;
|
||||
|
||||
// notify the world that the task is done
|
||||
Q_EMIT completed(true);
|
||||
|
||||
if (autoDelete()) {
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_SaveHistoryTask.cpp"
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
|
||||
SPDX-FileCopyrightText: 2009 Thomas Dreibholz <dreibh@iem.uni-due.de>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SAVEHISTORYTASK_H
|
||||
#define SAVEHISTORYTASK_H
|
||||
|
||||
#include "konsoleprivate_export.h"
|
||||
#include "session/SessionTask.h"
|
||||
|
||||
#include <kio/job.h>
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class TerminalCharacterDecoder;
|
||||
|
||||
/**
|
||||
* A task which prompts for a URL for each session and saves that session's output
|
||||
* to the given URL
|
||||
*/
|
||||
class KONSOLEPRIVATE_EXPORT SaveHistoryTask : public SessionTask
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/** Constructs a new task to save session output to URLs */
|
||||
explicit SaveHistoryTask(QObject *parent = nullptr);
|
||||
~SaveHistoryTask() override;
|
||||
|
||||
/**
|
||||
* Opens a save file dialog for each session in the group and begins saving
|
||||
* each session's history to the given URL.
|
||||
*
|
||||
* The data transfer is performed asynchronously and will continue after execute() returns.
|
||||
*/
|
||||
void execute() override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void jobDataRequested(KIO::Job *job, QByteArray &data);
|
||||
void jobResult(KJob *job);
|
||||
|
||||
private:
|
||||
class SaveJob // structure to keep information needed to process
|
||||
// incoming data requests from jobs
|
||||
{
|
||||
public:
|
||||
QPointer<Session> session; // the session associated with a history save job
|
||||
int lastLineFetched; // the last line processed in the previous data request
|
||||
// set this to -1 at the start of the save job
|
||||
|
||||
TerminalCharacterDecoder *decoder; // decoder used to convert terminal characters
|
||||
// into output
|
||||
};
|
||||
|
||||
QHash<KJob *, SaveJob> _jobSession;
|
||||
|
||||
static QString _saveDialogRecentURL;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,914 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SCREEN_H
|
||||
#define SCREEN_H
|
||||
|
||||
// STD
|
||||
#include <memory>
|
||||
|
||||
// Qt
|
||||
#include <QBitArray>
|
||||
#include <QRect>
|
||||
#include <QSet>
|
||||
#include <QVarLengthArray>
|
||||
#include <QVector>
|
||||
|
||||
// Konsole
|
||||
#include "../characters/Character.h"
|
||||
#include "konsoleprivate_export.h"
|
||||
|
||||
#define MODE_Origin 0
|
||||
#define MODE_Wrap 1
|
||||
#define MODE_Insert 2
|
||||
#define MODE_Screen 3
|
||||
#define MODE_Cursor 4
|
||||
#define MODE_NewLine 5
|
||||
#define MODE_AppScreen 6
|
||||
#define MODE_SelectCursor 7
|
||||
#define MODES_SCREEN 8
|
||||
|
||||
#define REPL_None 0
|
||||
#define REPL_PROMPT 1
|
||||
#define REPL_INPUT 2
|
||||
#define REPL_OUTPUT 3
|
||||
|
||||
struct TerminalGraphicsPlacement_t {
|
||||
QPixmap pixmap;
|
||||
qint64 id;
|
||||
qint64 pid;
|
||||
int z, X, Y, col, row, cols, rows;
|
||||
qreal opacity;
|
||||
bool scrolling;
|
||||
};
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class TerminalCharacterDecoder;
|
||||
class TerminalDisplay;
|
||||
class HistoryType;
|
||||
class HistoryScroll;
|
||||
class EscapeSequenceUrlExtractor;
|
||||
|
||||
/**
|
||||
\brief An image of characters with associated attributes.
|
||||
|
||||
The terminal emulation ( Emulation ) receives a serial stream of
|
||||
characters from the program currently running in the terminal.
|
||||
From this stream it creates an image of characters which is ultimately
|
||||
rendered by the display widget ( TerminalDisplay ). Some types of emulation
|
||||
may have more than one screen image.
|
||||
|
||||
getImage() is used to retrieve the currently visible image
|
||||
which is then used by the display widget to draw the output from the
|
||||
terminal.
|
||||
|
||||
The number of lines of output history which are kept in addition to the current
|
||||
screen image depends on the history scroll being used to store the output.
|
||||
The scroll is specified using setScroll()
|
||||
The output history can be retrieved using writeToStream()
|
||||
|
||||
The screen image has a selection associated with it, specified using
|
||||
setSelectionStart() and setSelectionEnd(). The selected text can be retrieved
|
||||
using selectedText(). When getImage() is used to retrieve the visible image,
|
||||
characters which are part of the selection have their colors inverted.
|
||||
*/
|
||||
class KONSOLEPRIVATE_EXPORT Screen
|
||||
{
|
||||
public:
|
||||
/* PlainText: Return plain text (default)
|
||||
* ConvertToHtml: Specifies if returned text should have HTML tags.
|
||||
* PreserveLineBreaks: Specifies whether new line characters should be
|
||||
* inserted into the returned text at the end of each terminal line.
|
||||
* TrimLeadingWhitespace: Specifies whether leading spaces should be
|
||||
* trimmed in the returned text.
|
||||
* TrimTrailingWhitespace: Specifies whether trailing spaces should be
|
||||
* trimmed in the returned text.
|
||||
*/
|
||||
enum DecodingOption {
|
||||
PlainText = 0x0,
|
||||
ConvertToHtml = 0x1,
|
||||
PreserveLineBreaks = 0x2,
|
||||
TrimLeadingWhitespace = 0x4,
|
||||
TrimTrailingWhitespace = 0x8,
|
||||
ExcludePrompt = 0x10,
|
||||
ExcludeInput = 0x20,
|
||||
ExcludeOutput = 0x40,
|
||||
};
|
||||
Q_DECLARE_FLAGS(DecodingOptions, DecodingOption)
|
||||
|
||||
/** Construct a new screen image of size @p lines by @p columns. */
|
||||
Screen(int lines, int columns);
|
||||
~Screen();
|
||||
|
||||
Screen(const Screen &) = delete;
|
||||
Screen &operator=(const Screen &) = delete;
|
||||
|
||||
EscapeSequenceUrlExtractor *urlExtractor() const;
|
||||
|
||||
// VT100/2 Operations
|
||||
// Cursor Movement
|
||||
|
||||
/**
|
||||
* Move the cursor up by @p n lines. The cursor will stop at the
|
||||
* top margin.
|
||||
*/
|
||||
void cursorUp(int n);
|
||||
/**
|
||||
* Move the cursor down by @p n lines. The cursor will stop at the
|
||||
* bottom margin.
|
||||
*/
|
||||
void cursorDown(int n);
|
||||
/**
|
||||
* Move the cursor to the left by @p n columns.
|
||||
* The cursor will stop at the first column.
|
||||
*/
|
||||
void cursorLeft(int n);
|
||||
/**
|
||||
* Moves cursor to beginning of the line by @p n lines down.
|
||||
* The cursor will stop at the beginning of the line.
|
||||
*/
|
||||
void cursorNextLine(int n);
|
||||
/**
|
||||
* Moves cursor to beginning of the line by @p n lines up.
|
||||
* The cursor will stop at the beginning of the line.
|
||||
*/
|
||||
void cursorPreviousLine(int n);
|
||||
/**
|
||||
* Move the cursor to the right by @p n columns.
|
||||
* The cursor will stop at the right-most column.
|
||||
*/
|
||||
void cursorRight(int n);
|
||||
/** Position the cursor on line @p y. */
|
||||
void setCursorY(int y);
|
||||
/** Position the cursor at column @p x. */
|
||||
void setCursorX(int x);
|
||||
/** Position the cursor at line @p y, column @p x. */
|
||||
void setCursorYX(int y, int x);
|
||||
|
||||
void initSelCursor();
|
||||
int selCursorUp(int n);
|
||||
int selCursorDown(int n);
|
||||
int selCursorLeft(int n);
|
||||
int selCursorRight(int n);
|
||||
|
||||
int selSetSelectionStart(int mode);
|
||||
int selSetSelectionEnd(int mode);
|
||||
|
||||
/**
|
||||
* Sets the margins for scrolling the screen.
|
||||
*
|
||||
* @param top The top line of the new scrolling margin.
|
||||
* @param bot The bottom line of the new scrolling margin.
|
||||
*/
|
||||
void setMargins(int top, int bot);
|
||||
/** Returns the top line of the scrolling region. */
|
||||
int topMargin() const;
|
||||
/** Returns the bottom line of the scrolling region. */
|
||||
int bottomMargin() const;
|
||||
|
||||
/**
|
||||
* Resets the scrolling margins back to the top and bottom lines
|
||||
* of the screen.
|
||||
*/
|
||||
void setDefaultMargins();
|
||||
|
||||
/**
|
||||
* Moves the cursor down one line, if the MODE_NewLine mode
|
||||
* flag is enabled then the cursor is returned to the leftmost
|
||||
* column first.
|
||||
*
|
||||
* Equivalent to NextLine() if the MODE_NewLine flag is set
|
||||
* or index() otherwise.
|
||||
*/
|
||||
void newLine();
|
||||
/**
|
||||
* Moves the cursor down one line and positions it at the beginning
|
||||
* of the line. Equivalent to calling Return() followed by index()
|
||||
*/
|
||||
void nextLine();
|
||||
|
||||
/**
|
||||
* Move the cursor down one line. If the cursor is on the bottom
|
||||
* line of the scrolling region (as returned by bottomMargin()) the
|
||||
* scrolling region is scrolled up by one line instead.
|
||||
*/
|
||||
void index();
|
||||
/**
|
||||
* Move the cursor up one line. If the cursor is on the top line
|
||||
* of the scrolling region (as returned by topMargin()) the scrolling
|
||||
* region is scrolled down by one line instead.
|
||||
*/
|
||||
void reverseIndex();
|
||||
|
||||
/**
|
||||
* Scroll the scrolling region of the screen up by @p n lines.
|
||||
* The scrolling region is initially the whole screen, but can be changed
|
||||
* using setMargins()
|
||||
*/
|
||||
void scrollUp(int n);
|
||||
/**
|
||||
* Scroll the scrolling region of the screen down by @p n lines.
|
||||
* The scrolling region is initially the whole screen, but can be changed
|
||||
* using setMargins()
|
||||
*/
|
||||
void scrollDown(int n);
|
||||
/**
|
||||
* Moves the cursor to the beginning of the current line.
|
||||
* Equivalent to setCursorX(0)
|
||||
*/
|
||||
void toStartOfLine();
|
||||
/**
|
||||
* Moves the cursor one column to the left and erases the character
|
||||
* at the new cursor position.
|
||||
*/
|
||||
void backspace();
|
||||
/** Moves the cursor @p n tab-stops to the right. */
|
||||
void tab(int n = 1);
|
||||
/** Moves the cursor @p n tab-stops to the left. */
|
||||
void backtab(int n);
|
||||
|
||||
// Editing
|
||||
|
||||
/**
|
||||
* Erase @p n characters beginning from the current cursor position.
|
||||
* This is equivalent to over-writing @p n characters starting with the current
|
||||
* cursor position with spaces.
|
||||
* If @p n is 0 then one character is erased.
|
||||
*/
|
||||
void eraseChars(int n);
|
||||
/**
|
||||
* Delete @p n characters beginning from the current cursor position.
|
||||
* If @p n is 0 then one character is deleted.
|
||||
*/
|
||||
void deleteChars(int n);
|
||||
/**
|
||||
* Insert @p n blank characters beginning from the current cursor position.
|
||||
* The position of the cursor is not altered.
|
||||
* If @p n is 0 then one character is inserted.
|
||||
*/
|
||||
void insertChars(int n);
|
||||
/**
|
||||
* Repeat the preceding graphic character @p n times, including SPACE.
|
||||
* If @p n is 0 then the character is repeated once.
|
||||
*/
|
||||
void repeatChars(int n);
|
||||
/**
|
||||
* Removes @p n lines beginning from the current cursor position.
|
||||
* The position of the cursor is not altered.
|
||||
* If @p n is 0 then one line is removed.
|
||||
*/
|
||||
void deleteLines(int n);
|
||||
/**
|
||||
* Inserts @p lines beginning from the current cursor position.
|
||||
* The position of the cursor is not altered.
|
||||
* If @p n is 0 then one line is inserted.
|
||||
*/
|
||||
void insertLines(int n);
|
||||
/** Clears all the tab stops. */
|
||||
void clearTabStops();
|
||||
/** Sets or removes a tab stop at the cursor's current column. */
|
||||
void changeTabStop(bool set);
|
||||
|
||||
/** Resets (clears) the specified screen @p m. */
|
||||
void resetMode(int m);
|
||||
/** Sets (enables) the specified screen @p m. */
|
||||
void setMode(int m);
|
||||
/**
|
||||
* Saves the state of the specified screen @p m. It can be restored
|
||||
* using restoreMode()
|
||||
*/
|
||||
void saveMode(int m);
|
||||
/** Restores the state of a screen @p m saved by calling saveMode() */
|
||||
void restoreMode(int m);
|
||||
/** Returns whether the specified screen @p me is enabled or not .*/
|
||||
bool getMode(int m) const;
|
||||
|
||||
/**
|
||||
* Saves the current position and appearance (text color and style) of the cursor.
|
||||
* It can be restored by calling restoreCursor()
|
||||
*/
|
||||
void saveCursor();
|
||||
/** Restores the position and appearance of the cursor. See saveCursor() */
|
||||
void restoreCursor();
|
||||
|
||||
/** Clear the whole screen, moving the current screen contents into the history first. */
|
||||
void clearEntireScreen();
|
||||
/**
|
||||
* Clear the area of the screen from the current cursor position to the end of
|
||||
* the screen.
|
||||
*/
|
||||
void clearToEndOfScreen();
|
||||
/**
|
||||
* Clear the area of the screen from the current cursor position to the start
|
||||
* of the screen.
|
||||
*/
|
||||
void clearToBeginOfScreen();
|
||||
/** Clears the whole of the line on which the cursor is currently positioned. */
|
||||
void clearEntireLine();
|
||||
/** Clears from the current cursor position to the end of the line. */
|
||||
void clearToEndOfLine();
|
||||
/** Clears from the current cursor position to the beginning of the line. */
|
||||
void clearToBeginOfLine();
|
||||
|
||||
/** Fills the entire screen with the letter 'E' */
|
||||
void helpAlign();
|
||||
|
||||
/**
|
||||
* Enables the given @p rendition flag. Rendition flags control the appearance
|
||||
* of characters on the screen.
|
||||
*
|
||||
* @see Character::rendition
|
||||
*/
|
||||
void setRendition(RenditionFlags rendition);
|
||||
void setUnderlineType(int type);
|
||||
/**
|
||||
* Disables the given @p rendition flag. Rendition flags control the appearance
|
||||
* of characters on the screen.
|
||||
*
|
||||
* @see Character::rendition
|
||||
*/
|
||||
void resetRendition(RenditionFlags rendition);
|
||||
|
||||
/**
|
||||
* Sets the cursor's foreground color.
|
||||
* @param space The color space used by the @p color argument
|
||||
* @param color The new foreground color. The meaning of this depends on
|
||||
* the color @p space used.
|
||||
*
|
||||
* @see CharacterColor
|
||||
*/
|
||||
void setForeColor(int space, int color);
|
||||
/**
|
||||
* Sets the cursor's background color.
|
||||
* @param space The color space used by the @p color argument.
|
||||
* @param color The new background color. The meaning of this depends on
|
||||
* the color @p space used.
|
||||
*
|
||||
* @see CharacterColor
|
||||
*/
|
||||
void setBackColor(int space, int color);
|
||||
/**
|
||||
* Resets the cursor's color back to the default and sets the
|
||||
* character's rendition flags back to the default settings.
|
||||
*/
|
||||
void setDefaultRendition();
|
||||
|
||||
void setULColor(int space, int color);
|
||||
|
||||
CharacterColor const *ulColorTable() const
|
||||
{
|
||||
return _ulColors;
|
||||
}
|
||||
|
||||
/** Returns the column which the cursor is positioned at. */
|
||||
int getCursorX() const;
|
||||
/** Returns the line which the cursor is positioned on. */
|
||||
int getCursorY() const;
|
||||
|
||||
/**
|
||||
* Resets the state of the screen. This resets the various screen modes
|
||||
* back to their default states. The cursor style and colors are reset
|
||||
* (as if setDefaultRendition() had been called)
|
||||
*
|
||||
* <ul>
|
||||
* <li>Line wrapping is enabled.</li>
|
||||
* <li>Origin mode is disabled.</li>
|
||||
* <li>Insert mode is disabled.</li>
|
||||
* <li>Cursor mode is enabled. TODO Document me</li>
|
||||
* <li>Screen mode is disabled. TODO Document me</li>
|
||||
* <li>New line mode is disabled. TODO Document me</li>
|
||||
* </ul>
|
||||
*
|
||||
* If @p softReset is true then perform a DECSTR,
|
||||
* otherwise perform RIS (Reset to Initial State).
|
||||
*
|
||||
* If @p preservePrompt is true, then attempt to preserve the
|
||||
* line with the command prompt even on a RIS.
|
||||
*/
|
||||
void reset(bool softReset = false, bool preservePrompt = false);
|
||||
|
||||
/**
|
||||
* Displays a new character at the current cursor position.
|
||||
*
|
||||
* If the cursor is currently positioned at the right-edge of the screen and
|
||||
* line wrapping is enabled then the character is added at the start of a new
|
||||
* line below the current one.
|
||||
*
|
||||
* If the MODE_Insert screen mode is currently enabled then the character
|
||||
* is inserted at the current cursor position, otherwise it will replace the
|
||||
* character already at the current cursor position.
|
||||
*/
|
||||
void displayCharacter(uint c);
|
||||
|
||||
/**
|
||||
* Resizes the image to a new fixed size of @p new_lines by @p new_columns.
|
||||
* In the case that @p new_columns is smaller than the current number of columns,
|
||||
* existing lines are not truncated. This prevents characters from being lost
|
||||
* if the terminal display is resized smaller and then larger again.
|
||||
*
|
||||
* The top and bottom margins are reset to the top and bottom of the new
|
||||
* screen size. Tab stops are also reset and the current selection is
|
||||
* cleared.
|
||||
*/
|
||||
void resizeImage(int new_lines, int new_columns);
|
||||
|
||||
/**
|
||||
* Returns the current screen image.
|
||||
* The result is an array of Characters of size [getLines()][getColumns()] which
|
||||
* must be freed by the caller after use.
|
||||
*
|
||||
* @param dest Buffer to copy the characters into
|
||||
* @param size Size of @p dest in Characters
|
||||
* @param startLine Index of first line to copy
|
||||
* @param endLine Index of last line to copy
|
||||
*/
|
||||
void getImage(Character *dest, int size, int startLine, int endLine) const;
|
||||
|
||||
/**
|
||||
* Returns the additional attributes associated with lines in the image.
|
||||
* The most important attribute is LINE_WRAPPED which specifies that the
|
||||
* line is wrapped,
|
||||
* other attributes control the size of characters in the line.
|
||||
*/
|
||||
QVector<LineProperty> getLineProperties(int startLine, int endLine) const;
|
||||
|
||||
/** Return the number of lines. */
|
||||
int getLines() const
|
||||
{
|
||||
return _lines;
|
||||
}
|
||||
|
||||
/** Return the number of columns. */
|
||||
int getColumns() const
|
||||
{
|
||||
return _columns;
|
||||
}
|
||||
|
||||
/** Return the number of lines in the history buffer. */
|
||||
int getHistLines() const;
|
||||
/**
|
||||
* Sets the type of storage used to keep lines in the history.
|
||||
* If @p copyPreviousScroll is true then the contents of the previous
|
||||
* history buffer are copied into the new scroll.
|
||||
*/
|
||||
void setScroll(const HistoryType &, bool copyPreviousScroll = true);
|
||||
/** Returns the type of storage used to keep lines in the history. */
|
||||
const HistoryType &getScroll() const;
|
||||
/**
|
||||
* Returns true if this screen keeps lines that are scrolled off the screen
|
||||
* in a history buffer.
|
||||
*/
|
||||
bool hasScroll() const;
|
||||
|
||||
/**
|
||||
* Sets the start of the selection.
|
||||
*
|
||||
* @param x The column index of the first character in the selection.
|
||||
* @param y The line index of the first character in the selection.
|
||||
* @param blockSelectionMode True if the selection is in column mode.
|
||||
*/
|
||||
void setSelectionStart(const int x, const int y, const bool blockSelectionMode);
|
||||
|
||||
/**
|
||||
* Sets the end of the current selection.
|
||||
*
|
||||
* @param x The column index of the last character in the selection.
|
||||
* @param y The line index of the last character in the selection.
|
||||
* @param trimTrailingWhitespace True if trailing whitespace is trimmed from the selection
|
||||
*/
|
||||
void setSelectionEnd(const int x, const int y, const bool trimTrailingWhitespace);
|
||||
|
||||
/**
|
||||
* Selects a range of characters with the same REPL mode as the character at (@p x, @p y)
|
||||
*/
|
||||
void selectReplContigious(const int x, const int y);
|
||||
|
||||
/**
|
||||
* Retrieves the start of the selection or the cursor position if there
|
||||
* is no selection.
|
||||
*/
|
||||
void getSelectionStart(int &column, int &line) const;
|
||||
|
||||
/**
|
||||
* Retrieves the end of the selection or the cursor position if there
|
||||
* is no selection.
|
||||
*/
|
||||
void getSelectionEnd(int &column, int &line) const;
|
||||
|
||||
/** Clears the current selection */
|
||||
void clearSelection();
|
||||
|
||||
/** Return the selection state */
|
||||
bool hasSelection() const;
|
||||
|
||||
/**
|
||||
* Returns true if the character at (@p x, @p y) is part of the
|
||||
* current selection.
|
||||
*/
|
||||
bool isSelected(const int x, const int y) const;
|
||||
|
||||
/**
|
||||
* Convenience method. Returns the currently selected text.
|
||||
* @param options See Screen::DecodingOptions
|
||||
*/
|
||||
QString selectedText(const DecodingOptions options) const;
|
||||
|
||||
/**
|
||||
* Convenience method. Returns the text between two indices.
|
||||
* @param startIndex Specifies the starting text index
|
||||
* @param endIndex Specifies the ending text index
|
||||
* @param options See Screen::DecodingOptions
|
||||
*/
|
||||
QString text(int startIndex, int endIndex, const DecodingOptions options) const;
|
||||
|
||||
/**
|
||||
* Copies part of the output to a stream.
|
||||
*
|
||||
* @param decoder A decoder which converts terminal characters into text
|
||||
* @param fromLine The first line in the history to retrieve
|
||||
* @param toLine The last line in the history to retrieve
|
||||
*/
|
||||
void writeLinesToStream(TerminalCharacterDecoder *decoder, int fromLine, int toLine) const;
|
||||
|
||||
/**
|
||||
* Checks if the text between from and to is inside the current
|
||||
* selection. If this is the case, the selection is cleared. The
|
||||
* from and to are coordinates in the current viewable window.
|
||||
* The loc(x,y) macro can be used to generate these values from a
|
||||
* column,line pair.
|
||||
*
|
||||
* @param from The start of the area to check.
|
||||
* @param to The end of the area to check
|
||||
*/
|
||||
void checkSelection(int from, int to);
|
||||
|
||||
/**
|
||||
* Sets or clears an attribute of the current line.
|
||||
*
|
||||
* @param property The attribute to set or clear
|
||||
* Possible properties are:
|
||||
* LINE_WRAPPED: Specifies that the line is wrapped.
|
||||
* LINE_DOUBLEWIDTH: Specifies that the characters in the current line
|
||||
* should be double the normal width.
|
||||
* LINE_DOUBLEHEIGHT_TOP:
|
||||
* LINE_DOUBLEHEIGHT_BOTTOM: Specifies that the characters in the current line
|
||||
* should be double the normal height.
|
||||
* Double-height lines are formed of two lines containing the same characters,
|
||||
* with the top one having the LINE_DOUBLEHEIGHT_TOP attribute
|
||||
* and the bottom one having the LINE_DOUBLEHEIGHT_BOTTOM attribute.
|
||||
* This allows other parts of the code to work on the
|
||||
* assumption that all lines are the same height.
|
||||
*
|
||||
* @param enable true to apply the attribute to the current line or false to remove it
|
||||
*/
|
||||
void setLineProperty(quint16 property, bool enable);
|
||||
|
||||
/**
|
||||
* Set REPL mode (shell integration)
|
||||
*
|
||||
* @param mode is the REPL mode
|
||||
* Possible modes are:
|
||||
* REPL_None
|
||||
* REPL_PROMPT
|
||||
* REPL_INPUT
|
||||
* REPL_OUTPUT
|
||||
*/
|
||||
void setReplMode(int mode);
|
||||
void setExitCode(int exitCode);
|
||||
/** Return true if semantic shell integration is in use. */
|
||||
bool hasRepl() const
|
||||
{
|
||||
return _hasRepl;
|
||||
}
|
||||
/** Return current REPL mode */
|
||||
int replMode() const
|
||||
{
|
||||
return _replMode;
|
||||
}
|
||||
/** Return location of current REPL mode start. */
|
||||
std::pair<int, int> replModeStart() const
|
||||
{
|
||||
return _replModeStart;
|
||||
}
|
||||
std::pair<int, int> replModeEnd() const
|
||||
{
|
||||
return _replModeEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of lines that the image has been scrolled up or down by,
|
||||
* since the last call to resetScrolledLines().
|
||||
*
|
||||
* a positive return value indicates that the image has been scrolled up,
|
||||
* a negative return value indicates that the image has been scrolled down.
|
||||
*/
|
||||
int scrolledLines() const;
|
||||
|
||||
/**
|
||||
* Returns the region of the image which was last scrolled.
|
||||
*
|
||||
* This is the area of the image from the top margin to the
|
||||
* bottom margin when the last scroll occurred.
|
||||
*/
|
||||
QRect lastScrolledRegion() const;
|
||||
|
||||
/**
|
||||
* Resets the count of the number of lines that the image has been scrolled up or down by,
|
||||
* see scrolledLines()
|
||||
*/
|
||||
void resetScrolledLines();
|
||||
|
||||
/**
|
||||
* Returns the number of lines of output which have been
|
||||
* dropped from the history since the last call
|
||||
* to resetDroppedLines()
|
||||
*
|
||||
* If the history is not unlimited then it will drop
|
||||
* the oldest lines of output if new lines are added when
|
||||
* it is full.
|
||||
*/
|
||||
int droppedLines() const;
|
||||
|
||||
/**
|
||||
* Resets the count of the number of lines dropped from
|
||||
* the history.
|
||||
*/
|
||||
void resetDroppedLines();
|
||||
|
||||
/**
|
||||
* Fills the buffer @p dest with @p count instances of the default (ie. blank)
|
||||
* Character style.
|
||||
*/
|
||||
static void fillWithDefaultChar(Character *dest, int count);
|
||||
|
||||
void setCurrentTerminalDisplay(TerminalDisplay *display)
|
||||
{
|
||||
_currentTerminalDisplay = display;
|
||||
}
|
||||
|
||||
TerminalDisplay *currentTerminalDisplay()
|
||||
{
|
||||
return _currentTerminalDisplay;
|
||||
}
|
||||
|
||||
QSet<uint> usedExtendedChars() const
|
||||
{
|
||||
QSet<uint> result;
|
||||
for (int i = 0; i < _lines; ++i) {
|
||||
const ImageLine &il = _screenLines[i];
|
||||
for (int j = 0; j < il.length(); ++j) {
|
||||
if (il[j].rendition.f.extended) {
|
||||
result << il[j].character;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void setEnableUrlExtractor(const bool enable);
|
||||
|
||||
static const Character DefaultChar;
|
||||
|
||||
// Return the total number of lines before resize (fix scroll glitch)
|
||||
int getOldTotalLines();
|
||||
// Return if it was a resize signal (fix scroll glitch)
|
||||
bool isResize();
|
||||
// Set reflow condition
|
||||
void setReflowLines(bool enable);
|
||||
|
||||
/* Graphics display functions */
|
||||
void addPlacement(QPixmap pixmap,
|
||||
int &rows,
|
||||
int &cols,
|
||||
int row = -1,
|
||||
int col = -1,
|
||||
bool scrolling = true,
|
||||
int moveCursor = 1,
|
||||
bool leaveText = false,
|
||||
int z = -1000,
|
||||
int id = -1,
|
||||
int pid = -1,
|
||||
qreal opacity = 1.0,
|
||||
int X = 0,
|
||||
int Y = 0);
|
||||
TerminalGraphicsPlacement_t *getGraphicsPlacement(unsigned int i);
|
||||
void delPlacements(int = 'a', qint64 = 0, qint64 = -1, int = 0, int = 0, int = 0);
|
||||
|
||||
bool hasGraphics() const
|
||||
{
|
||||
return _hasGraphics;
|
||||
}
|
||||
void setIgnoreWcWidth(bool ignore);
|
||||
|
||||
private:
|
||||
// copies a line of text from the screen or history into a stream using a
|
||||
// specified character decoder. Returns the number of lines actually copied,
|
||||
// which may be less than 'count' if (start+count) is more than the number of characters on
|
||||
// the line
|
||||
//
|
||||
// line - the line number to copy, from 0 (the earliest line in the history) up to
|
||||
// history->getLines() + lines - 1
|
||||
// start - the first column on the line to copy
|
||||
// count - the number of characters on the line to copy
|
||||
// decoder - a decoder which converts terminal characters (an Character array) into text
|
||||
// appendNewLine - if true a new line character (\n) is appended to the end of the line
|
||||
// isBlockSelectionMode - takes that in consideration while appending a new line.
|
||||
int copyLineToStream(int line,
|
||||
int start,
|
||||
int count,
|
||||
TerminalCharacterDecoder *decoder,
|
||||
bool appendNewLine,
|
||||
bool isBlockSelectionMode,
|
||||
const DecodingOptions options) const;
|
||||
|
||||
// fills a section of the screen image with the character 'c'
|
||||
// the parameters are specified as offsets from the start of the screen image.
|
||||
// the loc(x,y) macro can be used to generate these values from a column,line pair.
|
||||
// if resetLineRendition is true, all completely cleared lines will be set to single-width.
|
||||
void clearImage(int loca, int loce, char c, bool resetLineRendition = true);
|
||||
|
||||
// erases a rectangular section of the screen.
|
||||
void eraseBlock(int y, int x, int height, int width);
|
||||
|
||||
// move screen image between 'sourceBegin' and 'sourceEnd' to 'dest'.
|
||||
// the parameters are specified as offsets from the start of the screen image.
|
||||
// the loc(x,y) macro can be used to generate these values from a column,line pair.
|
||||
//
|
||||
// NOTE: moveImage() can only move whole lines
|
||||
void moveImage(int dest, int sourceBegin, int sourceEnd);
|
||||
// scroll up 'n' lines in current region, clearing the bottom 'n' lines
|
||||
void scrollUp(int from, int n);
|
||||
// scroll down 'n' lines in current region, clearing the top 'n' lines
|
||||
void scrollDown(int from, int n);
|
||||
|
||||
// when we handle scroll commands, we need to know which screenwindow will scroll
|
||||
TerminalDisplay *_currentTerminalDisplay;
|
||||
|
||||
void addHistLine();
|
||||
// add lines from _screen to _history and remove from _screen the added lines (used to resize lines and columns)
|
||||
void fastAddHistLine();
|
||||
|
||||
void initTabStops();
|
||||
|
||||
void updateEffectiveRendition();
|
||||
void reverseRendition(Character &p) const;
|
||||
|
||||
bool isSelectionValid() const;
|
||||
// copies text from 'startIndex' to 'endIndex' to a stream
|
||||
// startIndex and endIndex are positions generated using the loc(x,y) macro
|
||||
void writeToStream(TerminalCharacterDecoder *decoder, int startIndex, int endIndex, const DecodingOptions options) const;
|
||||
// copies 'count' lines from the screen buffer into 'dest',
|
||||
// starting from 'startLine', where 0 is the first line in the screen buffer
|
||||
void copyFromScreen(Character *dest, int startLine, int count) const;
|
||||
// copies 'count' lines from the history buffer into 'dest',
|
||||
// starting from 'startLine', where 0 is the first line in the history
|
||||
void copyFromHistory(Character *dest, int startLine, int count) const;
|
||||
|
||||
Character getCharacter(int col, int row) const;
|
||||
|
||||
// returns a buffer that can hold at most 'count' characters,
|
||||
// where the number of reallocations and object reinitializations
|
||||
// should be as minimal as possible
|
||||
static Character *getCharacterBuffer(const int size);
|
||||
|
||||
// Returns if its app mode or not
|
||||
inline bool isAppMode()
|
||||
{
|
||||
return _currentModes[MODE_AppScreen] == 1;
|
||||
}
|
||||
// Get the cursor line after checking if its app mode or not
|
||||
int getCursorLine();
|
||||
// Set the cursor line after checking if its app mode or not
|
||||
void setCursorLine(int newLine);
|
||||
|
||||
int getLineLength(const int line) const;
|
||||
|
||||
// returns the width in columns of the specified screen line,
|
||||
// taking DECDWL/DECDHL (double width/height modes) into account.
|
||||
int getScreenLineColumns(const int line) const;
|
||||
|
||||
// screen image ----------------
|
||||
int _lines;
|
||||
int _columns;
|
||||
|
||||
typedef QVector<Character> ImageLine; // [0..columns]
|
||||
std::vector<ImageLine> _screenLines; // [lines]
|
||||
int _screenLinesSize; // _screenLines.size()
|
||||
|
||||
int _scrolledLines;
|
||||
QRect _lastScrolledRegion;
|
||||
|
||||
int _droppedLines;
|
||||
|
||||
int _oldTotalLines;
|
||||
bool _isResize;
|
||||
bool _enableReflowLines;
|
||||
|
||||
std::vector<LineProperty> _lineProperties;
|
||||
LineProperty linePropertiesAt(unsigned int line);
|
||||
|
||||
// history buffer ---------------
|
||||
std::unique_ptr<HistoryScroll> _history;
|
||||
|
||||
// cursor location
|
||||
int _cuX;
|
||||
int _cuY;
|
||||
|
||||
// select mode cursor location
|
||||
int _selCuX{};
|
||||
int _selCuY{};
|
||||
|
||||
// cursor color and rendition info
|
||||
CharacterColor _currentForeground;
|
||||
CharacterColor _currentBackground;
|
||||
RenditionFlagsC _currentRendition;
|
||||
|
||||
CharacterColor _ulColors[15];
|
||||
int _ulColorQueueStart;
|
||||
int _ulColorQueueEnd;
|
||||
int _currentULColor;
|
||||
|
||||
// margins ----------------
|
||||
int _topMargin;
|
||||
int _bottomMargin;
|
||||
|
||||
// states ----------------
|
||||
int _currentModes[MODES_SCREEN];
|
||||
int _savedModes[MODES_SCREEN];
|
||||
int _replMode;
|
||||
bool _hasRepl;
|
||||
bool _replHadOutput;
|
||||
std::pair<int, int> _replModeStart;
|
||||
std::pair<int, int> _replModeEnd;
|
||||
std::pair<int, int> _replLastOutputStart;
|
||||
std::pair<int, int> _replLastOutputEnd;
|
||||
int commandCounter = 0;
|
||||
|
||||
// ----------------------------
|
||||
|
||||
QBitArray _tabStops;
|
||||
|
||||
// selection -------------------
|
||||
int _selBegin; // The first location selected.
|
||||
int _selTopLeft; // TopLeft Location.
|
||||
int _selBottomRight; // Bottom Right Location.
|
||||
bool _blockSelectionMode; // Column selection mode
|
||||
|
||||
// effective colors and rendition ------------
|
||||
CharacterColor _effectiveForeground; // These are derived from
|
||||
CharacterColor _effectiveBackground; // the cu_* variables above
|
||||
RenditionFlagsC _effectiveRendition; // to speed up operation
|
||||
|
||||
class SavedState
|
||||
{
|
||||
public:
|
||||
SavedState()
|
||||
: cursorColumn(0)
|
||||
, cursorLine(0)
|
||||
, originMode(0)
|
||||
, rendition({0})
|
||||
, foreground(CharacterColor())
|
||||
, background(CharacterColor())
|
||||
{
|
||||
}
|
||||
|
||||
int cursorColumn;
|
||||
int cursorLine;
|
||||
int originMode;
|
||||
RenditionFlagsC rendition;
|
||||
CharacterColor foreground;
|
||||
CharacterColor background;
|
||||
};
|
||||
SavedState _savedState;
|
||||
|
||||
// last position where we added a character
|
||||
int _lastPos;
|
||||
|
||||
// used in REP (repeating char)
|
||||
quint32 _lastDrawnChar;
|
||||
std::unique_ptr<EscapeSequenceUrlExtractor> _escapeSequenceUrlExtractor;
|
||||
void toggleUrlInput();
|
||||
|
||||
// Vt102Emulation defined max argument value that can be passed to a Screen function
|
||||
const int MAX_SCREEN_ARGUMENT = 40960;
|
||||
|
||||
/* Graphics */
|
||||
void addPlacement(std::unique_ptr<TerminalGraphicsPlacement_t> &u);
|
||||
std::vector<std::unique_ptr<TerminalGraphicsPlacement_t>> _graphicsPlacements;
|
||||
void scrollPlacements(int n, qint64 below = INT64_MAX, qint64 above = INT64_MAX);
|
||||
bool _hasGraphics;
|
||||
|
||||
//
|
||||
bool _ignoreWcWidth;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Screen::DecodingOptions)
|
||||
|
||||
}
|
||||
|
||||
#endif // SCREEN_H
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "ScreenWindow.h"
|
||||
|
||||
// Konsole
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
ScreenWindow::ScreenWindow(Screen *screen, QObject *parent)
|
||||
: QObject(parent)
|
||||
, _screen(nullptr)
|
||||
, _windowBuffer(nullptr)
|
||||
, _windowBufferSize(0)
|
||||
, _bufferNeedsUpdate(true)
|
||||
, _windowLines(1)
|
||||
, _currentLine(0)
|
||||
, _currentResultLine(-1)
|
||||
, _trackOutput(true)
|
||||
, _scrollCount(0)
|
||||
{
|
||||
setScreen(screen);
|
||||
}
|
||||
|
||||
ScreenWindow::~ScreenWindow()
|
||||
{
|
||||
delete[] _windowBuffer;
|
||||
}
|
||||
|
||||
void ScreenWindow::setScreen(Screen *screen)
|
||||
{
|
||||
Q_ASSERT(screen);
|
||||
|
||||
if (screen == _screen) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_EMIT screenAboutToChange();
|
||||
_screen = screen;
|
||||
}
|
||||
|
||||
Screen *ScreenWindow::screen() const
|
||||
{
|
||||
return _screen;
|
||||
}
|
||||
|
||||
Character *ScreenWindow::getImage()
|
||||
{
|
||||
// reallocate internal buffer if the window size has changed
|
||||
int size = windowLines() * windowColumns();
|
||||
if (_windowBuffer == nullptr || _windowBufferSize != size) {
|
||||
delete[] _windowBuffer;
|
||||
_windowBufferSize = size;
|
||||
_windowBuffer = new Character[size];
|
||||
_bufferNeedsUpdate = true;
|
||||
}
|
||||
|
||||
if (!_bufferNeedsUpdate) {
|
||||
return _windowBuffer;
|
||||
}
|
||||
|
||||
_screen->getImage(_windowBuffer, size, currentLine(), endWindowLine());
|
||||
|
||||
// this window may look beyond the end of the screen, in which
|
||||
// case there will be an unused area which needs to be filled
|
||||
// with blank characters
|
||||
fillUnusedArea();
|
||||
|
||||
_bufferNeedsUpdate = false;
|
||||
return _windowBuffer;
|
||||
}
|
||||
|
||||
void ScreenWindow::fillUnusedArea()
|
||||
{
|
||||
int screenEndLine = _screen->getHistLines() + _screen->getLines() - 1;
|
||||
int windowEndLine = currentLine() + windowLines() - 1;
|
||||
|
||||
int unusedLines = windowEndLine - screenEndLine;
|
||||
|
||||
// stop when unusedLines is negative; there is an issue w/ charsToFill
|
||||
// being greater than an int can hold
|
||||
if (unusedLines <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int charsToFill = unusedLines * windowColumns();
|
||||
|
||||
Screen::fillWithDefaultChar(_windowBuffer + _windowBufferSize - charsToFill, charsToFill);
|
||||
}
|
||||
|
||||
// return the index of the line at the end of this window, or if this window
|
||||
// goes beyond the end of the screen, the index of the line at the end
|
||||
// of the screen.
|
||||
//
|
||||
// when passing a line number to a Screen method, the line number should
|
||||
// never be more than endWindowLine()
|
||||
//
|
||||
int ScreenWindow::endWindowLine() const
|
||||
{
|
||||
return qMin(currentLine() + windowLines() - 1, lineCount() - 1);
|
||||
}
|
||||
|
||||
QVector<LineProperty> ScreenWindow::getLineProperties()
|
||||
{
|
||||
QVector<LineProperty> result = _screen->getLineProperties(currentLine(), endWindowLine());
|
||||
|
||||
if (result.count() != windowLines()) {
|
||||
result.resize(windowLines());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QString ScreenWindow::selectedText(const Screen::DecodingOptions options) const
|
||||
{
|
||||
return _screen->selectedText(options);
|
||||
}
|
||||
|
||||
void ScreenWindow::getSelectionStart(int &column, int &line)
|
||||
{
|
||||
_screen->getSelectionStart(column, line);
|
||||
line -= currentLine();
|
||||
}
|
||||
|
||||
void ScreenWindow::getSelectionEnd(int &column, int &line)
|
||||
{
|
||||
_screen->getSelectionEnd(column, line);
|
||||
line -= currentLine();
|
||||
}
|
||||
|
||||
void ScreenWindow::setSelectionStart(int column, int line, bool columnMode)
|
||||
{
|
||||
_screen->setSelectionStart(column, line + currentLine(), columnMode);
|
||||
|
||||
_bufferNeedsUpdate = true;
|
||||
Q_EMIT selectionChanged();
|
||||
}
|
||||
|
||||
void ScreenWindow::setSelectionEnd(int column, int line, bool trimTrailingWhitespace)
|
||||
{
|
||||
_screen->setSelectionEnd(column, line + currentLine(), trimTrailingWhitespace);
|
||||
|
||||
_bufferNeedsUpdate = true;
|
||||
Q_EMIT selectionChanged();
|
||||
}
|
||||
|
||||
void ScreenWindow::setSelectionByLineRange(int start, int end)
|
||||
{
|
||||
clearSelection();
|
||||
|
||||
_screen->setSelectionStart(0, start, false);
|
||||
_screen->setSelectionEnd(windowColumns(), end, false);
|
||||
|
||||
_bufferNeedsUpdate = true;
|
||||
Q_EMIT selectionChanged();
|
||||
}
|
||||
|
||||
bool ScreenWindow::isSelected(int column, int line)
|
||||
{
|
||||
return _screen->isSelected(column, qMin(line + currentLine(), endWindowLine()));
|
||||
}
|
||||
|
||||
void ScreenWindow::clearSelection()
|
||||
{
|
||||
_screen->clearSelection();
|
||||
|
||||
Q_EMIT selectionChanged();
|
||||
}
|
||||
|
||||
void ScreenWindow::setWindowLines(int lines)
|
||||
{
|
||||
Q_ASSERT(lines > 0);
|
||||
_windowLines = lines;
|
||||
}
|
||||
|
||||
int ScreenWindow::windowLines() const
|
||||
{
|
||||
return _windowLines;
|
||||
}
|
||||
|
||||
int ScreenWindow::windowColumns() const
|
||||
{
|
||||
return _screen->getColumns();
|
||||
}
|
||||
|
||||
int ScreenWindow::lineCount() const
|
||||
{
|
||||
return _screen->getHistLines() + _screen->getLines();
|
||||
}
|
||||
|
||||
int ScreenWindow::columnCount() const
|
||||
{
|
||||
return _screen->getColumns();
|
||||
}
|
||||
|
||||
QPoint ScreenWindow::cursorPosition() const
|
||||
{
|
||||
QPoint position;
|
||||
|
||||
position.setX(_screen->getCursorX());
|
||||
position.setY(_screen->getCursorY());
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
int ScreenWindow::currentLine() const
|
||||
{
|
||||
return qBound(0, _currentLine, std::max(0, lineCount() - windowLines()));
|
||||
}
|
||||
|
||||
int ScreenWindow::currentResultLine() const
|
||||
{
|
||||
return _currentResultLine;
|
||||
}
|
||||
|
||||
void ScreenWindow::setCurrentResultLine(int line)
|
||||
{
|
||||
if (_currentResultLine == line) {
|
||||
return;
|
||||
}
|
||||
|
||||
_currentResultLine = line;
|
||||
Q_EMIT currentResultLineChanged();
|
||||
}
|
||||
|
||||
void ScreenWindow::scrollBy(RelativeScrollMode mode, int amount, bool fullPage)
|
||||
{
|
||||
if (mode == ScrollLines) {
|
||||
scrollTo(currentLine() + amount);
|
||||
} else if (mode == ScrollPages || (mode == ScrollPrompts && !_screen->hasRepl())) {
|
||||
if (fullPage) {
|
||||
scrollTo(currentLine() + amount * (windowLines()));
|
||||
} else {
|
||||
scrollTo(currentLine() + amount * (windowLines() / 2));
|
||||
}
|
||||
} else if (mode == ScrollPrompts) {
|
||||
int i = currentLine();
|
||||
if (amount < 0) {
|
||||
QVector<LineProperty> properties = _screen->getLineProperties(0, currentLine());
|
||||
while (i > 0 && amount < 0) {
|
||||
i--;
|
||||
if ((properties[i].flags.f.prompt_start) != 0) {
|
||||
if (++amount == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (amount > 0) {
|
||||
QVector<LineProperty> properties = _screen->getLineProperties(currentLine(), _screen->getHistLines());
|
||||
while (i < _screen->getHistLines() && amount > 0) {
|
||||
i++;
|
||||
if ((properties[i - currentLine()].flags.f.prompt_start) != 0) {
|
||||
if (--amount == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scrollTo(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool ScreenWindow::atEndOfOutput() const
|
||||
{
|
||||
return currentLine() == (lineCount() - windowLines());
|
||||
}
|
||||
|
||||
void ScreenWindow::scrollTo(int line)
|
||||
{
|
||||
int maxCurrentLineNumber = lineCount() - windowLines();
|
||||
line = qBound(0, line, maxCurrentLineNumber);
|
||||
|
||||
const int delta = line - _currentLine;
|
||||
_currentLine = line;
|
||||
|
||||
// keep track of number of lines scrolled by,
|
||||
// this can be reset by calling resetScrollCount()
|
||||
_scrollCount += delta;
|
||||
|
||||
_bufferNeedsUpdate = true;
|
||||
|
||||
Q_EMIT scrolled(_currentLine);
|
||||
}
|
||||
|
||||
void ScreenWindow::setTrackOutput(bool trackOutput)
|
||||
{
|
||||
_trackOutput = trackOutput;
|
||||
}
|
||||
|
||||
bool ScreenWindow::trackOutput() const
|
||||
{
|
||||
return _trackOutput;
|
||||
}
|
||||
|
||||
int ScreenWindow::scrollCount() const
|
||||
{
|
||||
return _scrollCount;
|
||||
}
|
||||
|
||||
void ScreenWindow::resetScrollCount()
|
||||
{
|
||||
_scrollCount = 0;
|
||||
}
|
||||
|
||||
QRect ScreenWindow::scrollRegion() const
|
||||
{
|
||||
bool equalToScreenSize = windowLines() == _screen->getLines();
|
||||
|
||||
if (atEndOfOutput() && equalToScreenSize) {
|
||||
return _screen->lastScrolledRegion();
|
||||
}
|
||||
return {0, 0, windowColumns(), windowLines()};
|
||||
}
|
||||
|
||||
void ScreenWindow::updateCurrentLine()
|
||||
{
|
||||
if (!_screen->isResize()) {
|
||||
return;
|
||||
}
|
||||
if (_currentLine > 0) {
|
||||
_currentLine -= _screen->getOldTotalLines() - lineCount();
|
||||
}
|
||||
_currentLine = currentLine();
|
||||
}
|
||||
|
||||
void ScreenWindow::notifyOutputChanged()
|
||||
{
|
||||
// move window to the bottom of the screen and update scroll count
|
||||
// if this window is currently tracking the bottom of the screen
|
||||
if (_trackOutput) {
|
||||
_scrollCount -= _screen->scrolledLines();
|
||||
_currentLine = qMax(0, _screen->getHistLines() - (windowLines() - _screen->getLines()));
|
||||
} else {
|
||||
// if the history is not unlimited then it may
|
||||
// have run out of space and dropped the oldest
|
||||
// lines of output - in this case the screen
|
||||
// window's current line number will need to
|
||||
// be adjusted - otherwise the output will scroll
|
||||
_currentLine = qMax(0, _currentLine - _screen->droppedLines());
|
||||
|
||||
// ensure that the screen window's current position does
|
||||
// not go beyond the bottom of the screen
|
||||
_currentLine = qMin(_currentLine, _screen->getHistLines());
|
||||
}
|
||||
|
||||
_bufferNeedsUpdate = true;
|
||||
|
||||
Q_EMIT outputChanged();
|
||||
}
|
||||
|
||||
#include "moc_ScreenWindow.cpp"
|
||||
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SCREENWINDOW_H
|
||||
#define SCREENWINDOW_H
|
||||
|
||||
// Qt
|
||||
#include <QObject>
|
||||
#include <QPoint>
|
||||
#include <QRect>
|
||||
|
||||
// Konsole
|
||||
#include "../characters/Character.h"
|
||||
#include "Screen.h"
|
||||
#include "konsoleprivate_export.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
/**
|
||||
* Provides a window onto a section of a terminal screen. A terminal widget can then render
|
||||
* the contents of the window and use the window to change the terminal screen's selection
|
||||
* in response to mouse or keyboard input.
|
||||
*
|
||||
* A new ScreenWindow for a terminal session can be created by calling Emulation::createWindow()
|
||||
*
|
||||
* Use the scrollTo() method to scroll the window up and down on the screen.
|
||||
* Use the getImage() method to retrieve the character image which is currently visible in the window.
|
||||
*
|
||||
* setTrackOutput() controls whether the window moves to the bottom of the associated screen when new
|
||||
* lines are added to it.
|
||||
*
|
||||
* Whenever the output from the underlying screen is changed, the notifyOutputChanged() slot should
|
||||
* be called. This in turn will update the window's position and emit the outputChanged() signal
|
||||
* if necessary.
|
||||
*/
|
||||
class KONSOLEPRIVATE_EXPORT ScreenWindow : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a new screen window with the given parent.
|
||||
* A screen must be specified by calling setScreen() before calling getImage() or getLineProperties().
|
||||
*
|
||||
* You should not call this constructor directly, instead use the Emulation::createWindow() method
|
||||
* to create a window on the emulation which you wish to view. This allows the emulation
|
||||
* to notify the window when the associated screen has changed and synchronize selection updates
|
||||
* between all views on a session.
|
||||
*/
|
||||
explicit ScreenWindow(Screen *screen, QObject *parent = nullptr);
|
||||
~ScreenWindow() override;
|
||||
|
||||
/** Sets the screen which this window looks onto */
|
||||
void setScreen(Screen *screen);
|
||||
/** Returns the screen which this window looks onto */
|
||||
Screen *screen() const;
|
||||
|
||||
/**
|
||||
* Returns the image of characters which are currently visible through this window
|
||||
* onto the screen.
|
||||
*
|
||||
* The returned buffer is managed by the ScreenWindow instance and does not need to be
|
||||
* deleted by the caller.
|
||||
*/
|
||||
Character *getImage();
|
||||
|
||||
/**
|
||||
* Returns the line attributes associated with the lines of characters which
|
||||
* are currently visible through this window
|
||||
*/
|
||||
QVector<LineProperty> getLineProperties();
|
||||
|
||||
/**
|
||||
* Returns the number of lines which the region of the window
|
||||
* specified by scrollRegion() has been scrolled by since the last call
|
||||
* to resetScrollCount(). scrollRegion() is in most cases the
|
||||
* whole window, but will be a smaller area in, for example, applications
|
||||
* which provide split-screen facilities.
|
||||
*
|
||||
* This is not guaranteed to be accurate, but allows views to optimize
|
||||
* rendering by reducing the amount of costly text rendering that
|
||||
* needs to be done when the output is scrolled.
|
||||
*/
|
||||
int scrollCount() const;
|
||||
|
||||
/**
|
||||
* Resets the count of scrolled lines returned by scrollCount()
|
||||
*/
|
||||
void resetScrollCount();
|
||||
|
||||
/**
|
||||
* Returns the area of the window which was last scrolled, this is
|
||||
* usually the whole window area.
|
||||
*
|
||||
* Like scrollCount(), this is not guaranteed to be accurate,
|
||||
* but allows views to optimize rendering.
|
||||
*/
|
||||
QRect scrollRegion() const;
|
||||
|
||||
/**
|
||||
* What line the next search will start from
|
||||
*/
|
||||
void setCurrentResultLine(int line);
|
||||
int currentResultLine() const;
|
||||
|
||||
/**
|
||||
* Sets the start of the selection to the given @p line and @p column within
|
||||
* the window.
|
||||
*/
|
||||
void setSelectionStart(int column, int line, bool columnMode);
|
||||
/**
|
||||
* Sets the end of the selection to the given @p line and @p column within
|
||||
* the window.
|
||||
*/
|
||||
void setSelectionEnd(int column, int line, bool trimTrailingWhitespace);
|
||||
|
||||
/**
|
||||
* Sets the selection as the range specified by line @p start and line @p
|
||||
* end in the whole history.
|
||||
*
|
||||
* Both @p start and @p end are absolute line number in the whole history,
|
||||
* not relative line number in the window. This make it possible to select
|
||||
* range larger than the window . A good use case is selecting the whole
|
||||
* history.
|
||||
*/
|
||||
void setSelectionByLineRange(int start, int end);
|
||||
|
||||
/**
|
||||
* Retrieves the start of the selection within the window.
|
||||
*/
|
||||
void getSelectionStart(int &column, int &line);
|
||||
/**
|
||||
* Retrieves the end of the selection within the window.
|
||||
*/
|
||||
void getSelectionEnd(int &column, int &line);
|
||||
/**
|
||||
* Returns true if the character at @p line , @p column is part of the selection.
|
||||
*/
|
||||
bool isSelected(int column, int line);
|
||||
/**
|
||||
* Clears the current selection
|
||||
*/
|
||||
void clearSelection();
|
||||
|
||||
/** Sets the number of lines in the window */
|
||||
void setWindowLines(int lines);
|
||||
/** Returns the number of lines in the window */
|
||||
int windowLines() const;
|
||||
/** Returns the number of columns in the window */
|
||||
int windowColumns() const;
|
||||
|
||||
/** Returns the total number of lines in the screen */
|
||||
int lineCount() const;
|
||||
/** Returns the total number of columns in the screen */
|
||||
int columnCount() const;
|
||||
|
||||
/** Returns the index of the line which is currently at the top of this window */
|
||||
int currentLine() const;
|
||||
|
||||
/**
|
||||
* Returns the position of the cursor
|
||||
* within the window.
|
||||
*/
|
||||
QPoint cursorPosition() const;
|
||||
|
||||
/**
|
||||
* Convenience method. Returns true if the window is currently at the bottom
|
||||
* of the screen.
|
||||
*/
|
||||
bool atEndOfOutput() const;
|
||||
|
||||
/** Scrolls the window so that @p line is at the top of the window */
|
||||
void scrollTo(int line);
|
||||
|
||||
/** Describes the units which scrollBy() moves the window by. */
|
||||
enum RelativeScrollMode {
|
||||
/** Scroll the window down by a given number of lines. */
|
||||
ScrollLines,
|
||||
/**
|
||||
* Scroll the window down by a given number of pages, where
|
||||
* one page is windowLines() lines
|
||||
*/
|
||||
ScrollPages,
|
||||
/** Scroll by prompt (when using semantic shell integration)
|
||||
* scrolls up/down until the given number of new prompts appear.
|
||||
*/
|
||||
ScrollPrompts,
|
||||
};
|
||||
|
||||
/**
|
||||
* Scrolls the window relative to its current position on the screen.
|
||||
*
|
||||
* @param mode Specifies whether @p amount refers to the number of lines or the number
|
||||
* of pages to scroll.
|
||||
* @param amount The number of lines or pages ( depending on @p mode ) to scroll by. If
|
||||
* this number is positive, the view is scrolled down. If this number is negative, the view
|
||||
* is scrolled up.
|
||||
* @param fullPage Specifies whether to scroll by full page or half page.
|
||||
*/
|
||||
void scrollBy(RelativeScrollMode mode, int amount, bool fullPage);
|
||||
|
||||
/**
|
||||
* Specifies whether the window should automatically move to the bottom
|
||||
* of the screen when new output is added.
|
||||
*
|
||||
* If this is set to true, the window will be moved to the bottom of the associated screen ( see
|
||||
* screen() ) when the notifyOutputChanged() method is called.
|
||||
*/
|
||||
void setTrackOutput(bool trackOutput);
|
||||
/**
|
||||
* Returns whether the window automatically moves to the bottom of the screen as
|
||||
* new output is added. See setTrackOutput()
|
||||
*/
|
||||
bool trackOutput() const;
|
||||
|
||||
/**
|
||||
* Returns the text which is currently selected.
|
||||
*
|
||||
* @param options See Screen::DecodingOptions
|
||||
*/
|
||||
QString selectedText(const Screen::DecodingOptions options) const;
|
||||
|
||||
void updateCurrentLine();
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Notifies the window that the contents of the associated terminal screen have changed.
|
||||
* This moves the window to the bottom of the screen if trackOutput() is true and causes
|
||||
* the outputChanged() signal to be emitted.
|
||||
*/
|
||||
void notifyOutputChanged();
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when the contents of the associated terminal screen (see screen()) changes.
|
||||
*/
|
||||
void outputChanged();
|
||||
|
||||
void currentResultLineChanged();
|
||||
|
||||
/**
|
||||
* Emitted when the screen window is scrolled to a different position.
|
||||
*
|
||||
* @param line The line which is now at the top of the window.
|
||||
*/
|
||||
void scrolled(int line);
|
||||
|
||||
/** Emitted when the selection is changed. */
|
||||
void selectionChanged();
|
||||
|
||||
/** Emitted when e. g. Screen is changed (to alternate or primary), so
|
||||
* initial selection offsets etc. are invalidated.
|
||||
*/
|
||||
void screenAboutToChange();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(ScreenWindow)
|
||||
|
||||
int endWindowLine() const;
|
||||
void fillUnusedArea();
|
||||
|
||||
Screen *_screen; // see setScreen() , screen()
|
||||
Character *_windowBuffer;
|
||||
int _windowBufferSize;
|
||||
bool _bufferNeedsUpdate;
|
||||
|
||||
int _windowLines;
|
||||
int _currentLine; // see scrollTo() , currentLine()
|
||||
int _currentResultLine;
|
||||
bool _trackOutput; // see setTrackOutput() , trackOutput()
|
||||
int _scrollCount; // count of lines which the window has been scrolled by since
|
||||
// the last call to resetScrollCount()
|
||||
};
|
||||
}
|
||||
#endif // SCREENWINDOW_H
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Lindsay Roberts <os@lindsayr.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "ScrollState.h"
|
||||
|
||||
#include <QWheelEvent>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
void ScrollState::addWheelEvent(const QWheelEvent *wheel)
|
||||
{
|
||||
if ((wheel->angleDelta().y() != 0) && (wheel->pixelDelta().y() == 0)) {
|
||||
_remainingScrollPixel = 0;
|
||||
} else {
|
||||
_remainingScrollPixel += wheel->pixelDelta().y();
|
||||
}
|
||||
_remainingScrollAngle += wheel->angleDelta().y();
|
||||
}
|
||||
|
||||
void ScrollState::clearAll()
|
||||
{
|
||||
_remainingScrollPixel = 0;
|
||||
_remainingScrollAngle = 0;
|
||||
}
|
||||
|
||||
int ScrollState::consumeLegacySteps(int stepsize)
|
||||
{
|
||||
if (stepsize < 1) {
|
||||
stepsize = DEFAULT_ANGLE_SCROLL_LINE;
|
||||
}
|
||||
const int steps = _remainingScrollAngle / stepsize;
|
||||
_remainingScrollAngle -= steps * stepsize;
|
||||
if (steps != 0) {
|
||||
_remainingScrollPixel = 0;
|
||||
}
|
||||
return steps;
|
||||
}
|
||||
|
||||
int ScrollState::consumeSteps(int pixelStepSize, int angleStepSize)
|
||||
{
|
||||
if (_remainingScrollPixel != 0) {
|
||||
int steps = _remainingScrollPixel / pixelStepSize;
|
||||
_remainingScrollPixel -= steps * pixelStepSize;
|
||||
_remainingScrollAngle = 0;
|
||||
return steps;
|
||||
}
|
||||
int steps = _remainingScrollAngle / angleStepSize;
|
||||
_remainingScrollAngle -= steps * angleStepSize;
|
||||
_remainingScrollPixel = 0;
|
||||
return steps;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Lindsay Roberts <os@lindsayr.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SCROLLSTATE_H
|
||||
#define SCROLLSTATE_H
|
||||
|
||||
class QWheelEvent;
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
/**
|
||||
* Represents accumulation of wheel scroll from scroll events.
|
||||
*
|
||||
* Modern high precision scroll events supply many smaller events
|
||||
* that may or may not translate into a UI action to support smooth
|
||||
* pixel level scrolling. Builtin classes such as QScrollBar
|
||||
* support these events under Qt5, but custom code
|
||||
* written to handle scroll events in other ways must be modified
|
||||
* to accumulate small deltas and act when suitable thresholds have
|
||||
* been reached (ideally 1 for pixel scroll values towards any action
|
||||
* that can be mapped to a pixel movement).
|
||||
*/
|
||||
struct ScrollState {
|
||||
enum {
|
||||
DEFAULT_ANGLE_SCROLL_LINE = 120,
|
||||
};
|
||||
enum {
|
||||
DEGREES_PER_ANGLE_UNIT = 8,
|
||||
};
|
||||
static inline int degreesToAngle(const int angle)
|
||||
{
|
||||
return angle * DEGREES_PER_ANGLE_UNIT;
|
||||
}
|
||||
|
||||
int angle() const
|
||||
{
|
||||
return _remainingScrollAngle;
|
||||
}
|
||||
|
||||
int pixel() const
|
||||
{
|
||||
return _remainingScrollPixel;
|
||||
}
|
||||
|
||||
/** Add scroll values from a QWheelEvent to the accumulated totals */
|
||||
void addWheelEvent(const QWheelEvent *wheel);
|
||||
|
||||
/** Clear all - used when scroll is consumed by another class like QScrollBar */
|
||||
void clearAll();
|
||||
/** Return the (signed) multiple of @p stepsize available and subtract it from
|
||||
* accumulated totals. Also clears accumulated pixel scroll. */
|
||||
int consumeLegacySteps(int stepsize);
|
||||
/** Return the (signed) multiple of @p pixelStepSize if any pixel scroll is
|
||||
* available - that is, if pixel scroll is being supplied, or the same from
|
||||
* the @p angleStepSize else. The corresponding value is subtracted from
|
||||
* the accumulated total. The other scroll style value is cleared. */
|
||||
int consumeSteps(int pixelStepSize, int angleStepSize);
|
||||
|
||||
ScrollState()
|
||||
: _remainingScrollAngle(0)
|
||||
, _remainingScrollPixel(0)
|
||||
{
|
||||
}
|
||||
|
||||
int _remainingScrollAngle;
|
||||
int _remainingScrollPixel;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SCROLLSTATE_H
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
|
||||
SPDX-FileCopyrightText: 2009 Thomas Dreibholz <dreibh@iem.uni-due.de>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "SearchHistoryTask.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "../decoders/PlainTextDecoder.h"
|
||||
#include "Emulation.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
void SearchHistoryTask::addScreenWindow(Session *session, ScreenWindow *searchWindow)
|
||||
{
|
||||
_windows.insert(session, searchWindow);
|
||||
}
|
||||
|
||||
void SearchHistoryTask::execute()
|
||||
{
|
||||
auto iter = QMapIterator<QPointer<Session>, ScreenWindowPtr>(_windows);
|
||||
|
||||
while (iter.hasNext()) {
|
||||
iter.next();
|
||||
executeOnScreenWindow(iter.key(), iter.value());
|
||||
}
|
||||
|
||||
if (autoDelete()) {
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void SearchHistoryTask::executeOnScreenWindow(const QPointer<Session> &session, const ScreenWindowPtr &window)
|
||||
{
|
||||
Q_ASSERT(session);
|
||||
Q_ASSERT(window);
|
||||
|
||||
Emulation *emulation = session->emulation();
|
||||
|
||||
if (!_regExp.pattern().isEmpty()) {
|
||||
int pos = -1;
|
||||
bool forwards = (_direction == Enum::ForwardsSearch);
|
||||
const int lastLine = window->lineCount() - 1;
|
||||
|
||||
int startLine = _startLine;
|
||||
if (forwards && (_startLine == lastLine)) {
|
||||
if (!_noWrap) {
|
||||
startLine = 0;
|
||||
}
|
||||
} else if (!forwards && (_startLine == 0)) {
|
||||
if (!_noWrap) {
|
||||
startLine = lastLine;
|
||||
}
|
||||
} else {
|
||||
startLine = _startLine + (forwards ? 1 : -1);
|
||||
}
|
||||
|
||||
QString string;
|
||||
|
||||
// text stream to read history into string for pattern or regular expression searching
|
||||
QTextStream searchStream(&string);
|
||||
|
||||
PlainTextDecoder decoder;
|
||||
decoder.setRecordLinePositions(true);
|
||||
|
||||
// setup first and last lines depending on search direction
|
||||
int line = startLine;
|
||||
|
||||
// read through and search history in blocks of 10K lines.
|
||||
// this balances the need to retrieve lots of data from the history each time
|
||||
//(for efficient searching)
|
||||
// without using silly amounts of memory if the history is very large.
|
||||
const int maxDelta = qMin(window->lineCount(), 10000);
|
||||
int delta = forwards ? maxDelta : -maxDelta;
|
||||
|
||||
int endLine = line;
|
||||
bool hasWrapped = false; // set to true when we reach the top/bottom
|
||||
// of the output and continue from the other
|
||||
// end
|
||||
|
||||
// loop through history in blocks of <delta> lines.
|
||||
do {
|
||||
// ensure that application does not appear to hang
|
||||
// if searching through a lengthy output
|
||||
QApplication::processEvents();
|
||||
|
||||
// calculate lines to search in this iteration
|
||||
if (hasWrapped) {
|
||||
if (endLine == lastLine) {
|
||||
line = 0;
|
||||
} else if (endLine == 0) {
|
||||
line = lastLine;
|
||||
}
|
||||
|
||||
endLine += delta;
|
||||
|
||||
if (forwards) {
|
||||
endLine = qMin(startLine, endLine);
|
||||
} else {
|
||||
endLine = qMax(startLine, endLine);
|
||||
}
|
||||
} else {
|
||||
endLine += delta;
|
||||
|
||||
if (endLine > lastLine) {
|
||||
hasWrapped = true;
|
||||
endLine = lastLine;
|
||||
} else if (endLine < 0) {
|
||||
hasWrapped = true;
|
||||
endLine = 0;
|
||||
}
|
||||
}
|
||||
|
||||
decoder.begin(&searchStream);
|
||||
emulation->writeToStream(&decoder, qMin(endLine, line), qMax(endLine, line));
|
||||
decoder.end();
|
||||
|
||||
// line number search below assumes that the buffer ends with a new-line
|
||||
string.append(QLatin1Char('\n'));
|
||||
|
||||
if (forwards) {
|
||||
pos = string.indexOf(_regExp);
|
||||
} else {
|
||||
pos = string.lastIndexOf(_regExp);
|
||||
}
|
||||
|
||||
// if a match is found, position the cursor on that line and update the screen
|
||||
if (pos != -1) {
|
||||
int newLines = 0;
|
||||
QList<int> linePositions = decoder.linePositions();
|
||||
while (newLines < linePositions.count() && linePositions[newLines] <= pos) {
|
||||
newLines++;
|
||||
}
|
||||
|
||||
// ignore the new line at the start of the buffer
|
||||
newLines--;
|
||||
|
||||
int findPos = qMin(line, endLine) + newLines;
|
||||
|
||||
highlightResult(window, findPos);
|
||||
|
||||
Q_EMIT completed(true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// if noWrap is checked and we wrapped, set cursor at last match
|
||||
if (hasWrapped && _noWrap) {
|
||||
// invert search direction
|
||||
forwards = !forwards;
|
||||
delta = -delta;
|
||||
endLine += (forwards ? 1 : -1);
|
||||
hasWrapped = false;
|
||||
}
|
||||
|
||||
// clear the current block of text and move to the next one
|
||||
string.clear();
|
||||
line = endLine;
|
||||
} while (startLine != endLine);
|
||||
if (!session->getSelectMode()) {
|
||||
// if no match was found, clear selection to indicate this,
|
||||
window->clearSelection();
|
||||
window->notifyOutputChanged();
|
||||
}
|
||||
}
|
||||
|
||||
Q_EMIT completed(false);
|
||||
}
|
||||
void SearchHistoryTask::highlightResult(const ScreenWindowPtr &window, int findPos)
|
||||
{
|
||||
// work out how many lines into the current block of text the search result was found
|
||||
//- looks a little painful, but it only has to be done once per search.
|
||||
|
||||
////qDebug() << "Found result at line " << findPos;
|
||||
|
||||
// update display to show area of history containing selection
|
||||
if ((findPos < window->currentLine()) || (findPos >= (window->currentLine() + window->windowLines()))) {
|
||||
int centeredScrollPos = findPos - window->windowLines() / 2;
|
||||
if (centeredScrollPos < 0) {
|
||||
centeredScrollPos = 0;
|
||||
}
|
||||
|
||||
window->scrollTo(centeredScrollPos);
|
||||
}
|
||||
|
||||
window->setTrackOutput(false);
|
||||
window->notifyOutputChanged();
|
||||
window->setCurrentResultLine(findPos);
|
||||
}
|
||||
|
||||
SearchHistoryTask::SearchHistoryTask(QObject *parent)
|
||||
: SessionTask(parent)
|
||||
, _direction(Enum::BackwardsSearch)
|
||||
, _noWrap(false)
|
||||
, _startLine(0)
|
||||
{
|
||||
}
|
||||
|
||||
void SearchHistoryTask::setSearchDirection(Enum::SearchDirection direction)
|
||||
{
|
||||
_direction = direction;
|
||||
}
|
||||
|
||||
void SearchHistoryTask::setStartLine(int line)
|
||||
{
|
||||
_startLine = line;
|
||||
}
|
||||
|
||||
Enum::SearchDirection SearchHistoryTask::searchDirection() const
|
||||
{
|
||||
return _direction;
|
||||
}
|
||||
|
||||
void SearchHistoryTask::setRegExp(const QRegularExpression &expression)
|
||||
{
|
||||
_regExp = expression;
|
||||
}
|
||||
|
||||
QRegularExpression SearchHistoryTask::regExp() const
|
||||
{
|
||||
return _regExp;
|
||||
}
|
||||
|
||||
void SearchHistoryTask::setNoWrap(bool noWrap)
|
||||
{
|
||||
_noWrap = noWrap;
|
||||
}
|
||||
|
||||
bool SearchHistoryTask::noWrap() const
|
||||
{
|
||||
return _noWrap;
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_SearchHistoryTask.cpp"
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
|
||||
SPDX-FileCopyrightText: 2009 Thomas Dreibholz <dreibh@iem.uni-due.de>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SEARCHHISTORYTASK_H
|
||||
#define SEARCHHISTORYTASK_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QPointer>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "Enumeration.h"
|
||||
#include "ScreenWindow.h"
|
||||
#include "konsoleprivate_export.h"
|
||||
#include "session/Session.h"
|
||||
#include "session/SessionTask.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
// class SearchHistoryThread;
|
||||
/**
|
||||
* A task which searches through the output of sessions for matches for a given regular expression.
|
||||
* SearchHistoryTask operates on ScreenWindow instances rather than sessions added by addSession().
|
||||
* A screen window can be added to the list to search using addScreenWindow()
|
||||
*
|
||||
* When execute() is called, the search begins in the direction specified by searchDirection(),
|
||||
* starting at the position of the current selection.
|
||||
*
|
||||
* FIXME - This is not a proper implementation of SessionTask, in that it ignores sessions specified
|
||||
* with addSession()
|
||||
*
|
||||
* TODO - Implementation requirements:
|
||||
* May provide progress feedback to the user when searching very large output logs.
|
||||
*/
|
||||
class KONSOLEPRIVATE_EXPORT SearchHistoryTask : public SessionTask
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a new search task.
|
||||
*/
|
||||
explicit SearchHistoryTask(QObject *parent = nullptr);
|
||||
|
||||
/** Adds a screen window to the list to search when execute() is called. */
|
||||
void addScreenWindow(Session *session, ScreenWindow *searchWindow);
|
||||
|
||||
/** Sets the regular expression which is searched for when execute() is called */
|
||||
void setRegExp(const QRegularExpression &expression);
|
||||
/** Returns the regular expression which is searched for when execute() is called */
|
||||
QRegularExpression regExp() const;
|
||||
|
||||
/** Specifies the direction to search in when execute() is called. */
|
||||
void setSearchDirection(Enum::SearchDirection direction);
|
||||
/** Returns the current search direction. See setSearchDirection(). */
|
||||
Enum::SearchDirection searchDirection() const;
|
||||
|
||||
/** Specifies if search should wrap when execute() is called. */
|
||||
void setNoWrap(bool noWrap);
|
||||
/** Returns the current noWrap value. See setNoWrap(). */
|
||||
bool noWrap() const;
|
||||
|
||||
/** The line from which the search will be done **/
|
||||
void setStartLine(int line);
|
||||
|
||||
/**
|
||||
* Performs a search through the session's history, starting at the position
|
||||
* of the current selection, in the direction specified by setSearchDirection().
|
||||
*
|
||||
* If it finds a match, the ScreenWindow specified in the constructor is
|
||||
* scrolled to the position where the match occurred and the selection
|
||||
* is set to the matching text. execute() then returns immediately.
|
||||
*
|
||||
* To continue the search looking for further matches, call execute() again.
|
||||
*/
|
||||
void execute() override;
|
||||
|
||||
private:
|
||||
using ScreenWindowPtr = QPointer<ScreenWindow>;
|
||||
|
||||
void executeOnScreenWindow(const QPointer<Session> &session, const ScreenWindowPtr &window);
|
||||
void highlightResult(const ScreenWindowPtr &window, int findPos);
|
||||
|
||||
QMap<QPointer<Session>, ScreenWindowPtr> _windows;
|
||||
QRegularExpression _regExp;
|
||||
Enum::SearchDirection _direction;
|
||||
bool _noWrap;
|
||||
int _startLine;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "ShellCommand.h"
|
||||
|
||||
// KDE
|
||||
#include <KShell>
|
||||
|
||||
using Konsole::ShellCommand;
|
||||
|
||||
ShellCommand::ShellCommand(const QString &aCommand)
|
||||
: _arguments(KShell::splitArgs(aCommand))
|
||||
{
|
||||
}
|
||||
|
||||
ShellCommand::ShellCommand(const QString &aCommand, const QStringList &aArguments)
|
||||
: _arguments(aArguments)
|
||||
{
|
||||
if (!_arguments.isEmpty()) {
|
||||
_arguments[0] = aCommand;
|
||||
}
|
||||
}
|
||||
|
||||
QString ShellCommand::fullCommand() const
|
||||
{
|
||||
QStringList quotedArgs(_arguments);
|
||||
for (int i = 0; i < quotedArgs.count(); i++) {
|
||||
QString arg = quotedArgs.at(i);
|
||||
bool hasSpace = false;
|
||||
for (int j = 0; j < arg.length(); j++) {
|
||||
if (arg[j].isSpace()) {
|
||||
hasSpace = true;
|
||||
}
|
||||
}
|
||||
if (hasSpace) {
|
||||
quotedArgs[i] = QLatin1Char('\"') + arg + QLatin1Char('\"');
|
||||
}
|
||||
}
|
||||
return quotedArgs.join(QLatin1Char(' '));
|
||||
}
|
||||
|
||||
QString ShellCommand::command() const
|
||||
{
|
||||
if (!_arguments.isEmpty()) {
|
||||
return _arguments[0];
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QStringList ShellCommand::arguments() const
|
||||
{
|
||||
return _arguments;
|
||||
}
|
||||
|
||||
QStringList ShellCommand::expand(const QStringList &items)
|
||||
{
|
||||
QStringList result;
|
||||
result.reserve(items.size());
|
||||
|
||||
for (const QString &item : items) {
|
||||
result << expand(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QString ShellCommand::expand(const QString &text)
|
||||
{
|
||||
QString result = text;
|
||||
expandEnv(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ShellCommand::isValidEnvCharacter(const QChar &ch)
|
||||
{
|
||||
const ushort code = ch.unicode();
|
||||
return isValidLeadingEnvCharacter(ch) || ('0' <= code && code <= '9');
|
||||
}
|
||||
|
||||
bool ShellCommand::isValidLeadingEnvCharacter(const QChar &ch)
|
||||
{
|
||||
const ushort code = ch.unicode();
|
||||
return (code == '_') || ('A' <= code && code <= 'Z');
|
||||
}
|
||||
|
||||
/*
|
||||
* expandEnv
|
||||
*
|
||||
* Expand environment variables in text. Escaped '$' characters are ignored.
|
||||
* Return true if any variables were expanded
|
||||
*/
|
||||
bool ShellCommand::expandEnv(QString &text)
|
||||
{
|
||||
const QLatin1Char dollarChar('$');
|
||||
const QLatin1Char backslashChar('\\');
|
||||
|
||||
int dollarPos = 0;
|
||||
bool expanded = false;
|
||||
|
||||
// find and expand all environment variables beginning with '$'
|
||||
while ((dollarPos = text.indexOf(dollarChar, dollarPos)) != -1) {
|
||||
// if '$' is the last character, there is no way of expanding
|
||||
if (dollarPos == text.length() - 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
// skip escaped '$'
|
||||
if (dollarPos > 0 && text.at(dollarPos - 1) == backslashChar) {
|
||||
dollarPos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// if '$' is followed by an invalid leading character, skip this '$'
|
||||
if (!isValidLeadingEnvCharacter(text.at(dollarPos + 1))) {
|
||||
dollarPos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
int endPos = dollarPos + 1;
|
||||
Q_ASSERT(endPos < text.length());
|
||||
while (endPos < text.length() && isValidEnvCharacter(text.at(endPos))) {
|
||||
endPos++;
|
||||
}
|
||||
|
||||
const int len = endPos - dollarPos;
|
||||
const QString key = text.mid(dollarPos + 1, len - 1);
|
||||
const QString value = QString::fromLocal8Bit(qgetenv(key.toLocal8Bit().constData()));
|
||||
|
||||
if (!value.isEmpty()) {
|
||||
text.replace(dollarPos, len, value);
|
||||
expanded = true;
|
||||
dollarPos = dollarPos + value.length();
|
||||
} else {
|
||||
dollarPos = endPos;
|
||||
}
|
||||
}
|
||||
|
||||
return expanded;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SHELLCOMMAND_H
|
||||
#define SHELLCOMMAND_H
|
||||
|
||||
// Qt
|
||||
#include <QStringList>
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
/**
|
||||
* A class to parse and extract information about shell commands.
|
||||
*
|
||||
* ShellCommand can be used to:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Take a command-line (eg "/bin/sh -c /path/to/my/script") and split it
|
||||
* into its component parts (eg. the command "/bin/sh" and the arguments
|
||||
* "-c","/path/to/my/script")
|
||||
* </li>
|
||||
* <li>Take a command and a list of arguments and combine them to
|
||||
* form a complete command line.
|
||||
* </li>
|
||||
* <li>Determine whether the binary specified by a command exists in the
|
||||
* user's PATH.
|
||||
* </li>
|
||||
* <li>Determine whether a command-line specifies the execution of
|
||||
* another command as the root user using su/sudo etc.
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
class ShellCommand
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructs a ShellCommand from a command line.
|
||||
*
|
||||
* @param aCommand The command line to parse.
|
||||
*/
|
||||
explicit ShellCommand(const QString &aCommand);
|
||||
/**
|
||||
* Constructs a ShellCommand with the specified @p aCommand and @p aArguments.
|
||||
*/
|
||||
ShellCommand(const QString &aCommand, const QStringList &aArguments);
|
||||
|
||||
/** Returns the command. */
|
||||
QString command() const;
|
||||
/** Returns the arguments. */
|
||||
QStringList arguments() const;
|
||||
|
||||
/**
|
||||
* Returns the full command line.
|
||||
*/
|
||||
QString fullCommand() const;
|
||||
|
||||
/** Expands environment variables in @p text .*/
|
||||
static QString expand(const QString &text);
|
||||
|
||||
/** Expands environment variables in each string in @p list. */
|
||||
static QStringList expand(const QStringList &items);
|
||||
|
||||
static bool isValidEnvCharacter(const QChar &ch);
|
||||
|
||||
static bool isValidLeadingEnvCharacter(const QChar &ch);
|
||||
|
||||
private:
|
||||
static bool expandEnv(QString &text);
|
||||
|
||||
QStringList _arguments;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SHELLCOMMAND_H
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 René J.V. Bertin <rjvbertin@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SHORTCUT_H
|
||||
#define SHORTCUT_H
|
||||
|
||||
#include <qnamespace.h>
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
/**
|
||||
* Platform-specific main shortcut "opcode":
|
||||
*/
|
||||
enum Modifier {
|
||||
#ifdef Q_OS_MACOS
|
||||
// Use plain Command key for shortcuts
|
||||
ACCEL = Qt::CTRL,
|
||||
#else
|
||||
// Use Ctrl+Shift for shortcuts
|
||||
ACCEL = Qt::CTRL | Qt::SHIFT,
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SHORTCUT_H
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
This source file is part of Konsole, a terminal emulator.
|
||||
|
||||
SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "ShouldApplyProperty.h"
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
ShouldApplyProperty::ShouldApplyProperty(const Profile::Ptr &profile, bool modifiedOnly)
|
||||
: _profile(profile)
|
||||
, _modifiedPropertiesOnly(modifiedOnly)
|
||||
{
|
||||
}
|
||||
|
||||
bool ShouldApplyProperty::shouldApply(Profile::Property property) const
|
||||
{
|
||||
return !_modifiedPropertiesOnly || _profile->isPropertySet(property);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
This source file is part of Konsole, a terminal emulator.
|
||||
|
||||
SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SHOULDAPPLYPROPERTY_H
|
||||
#define SHOULDAPPLYPROPERTY_H
|
||||
|
||||
#include "konsoleprivate_export.h"
|
||||
#include "profile/Profile.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
/** Utility class to simplify code in SessionManager::applyProfile(). */
|
||||
class KONSOLEPRIVATE_EXPORT ShouldApplyProperty
|
||||
{
|
||||
public:
|
||||
ShouldApplyProperty(const Profile::Ptr &profile, bool modifiedOnly);
|
||||
|
||||
bool shouldApply(Profile::Property property) const;
|
||||
|
||||
private:
|
||||
const Profile::Ptr _profile;
|
||||
bool _modifiedPropertiesOnly;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "UnixProcessInfo.h"
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
// Unix
|
||||
#include <arpa/inet.h>
|
||||
#include <cerrno>
|
||||
#include <netinet/in.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Qt
|
||||
#include <QDebug>
|
||||
#include <QtGlobal>
|
||||
|
||||
#if defined(Q_OS_FREEBSD) || defined(Q_OS_OPEN_BSD) || defined(Q_OS_MACOS)
|
||||
#include <QSharedPointer>
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
UnixProcessInfo::UnixProcessInfo(int pid)
|
||||
: ProcessInfo(pid)
|
||||
{
|
||||
setUserNameRequired(true);
|
||||
}
|
||||
|
||||
void UnixProcessInfo::readProcessInfo(int pid)
|
||||
{
|
||||
// prevent _arguments from growing longer and longer each time this
|
||||
// method is called.
|
||||
clearArguments();
|
||||
|
||||
if (readProcInfo(pid)) {
|
||||
readArguments(pid);
|
||||
readCurrentDir(pid);
|
||||
|
||||
bool ok = false;
|
||||
const QString &processNameString = name(&ok);
|
||||
|
||||
if (ok && processNameString == QLatin1String("sudo")) {
|
||||
// Append process name along with sudo
|
||||
const QVector<QString> &args = arguments(&ok);
|
||||
|
||||
if (ok && args.size() > 1) {
|
||||
setName(processNameString + QStringLiteral(" ") + args[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnixProcessInfo::readUserName()
|
||||
{
|
||||
bool ok = false;
|
||||
const int uid = userId(&ok);
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct passwd passwdStruct;
|
||||
struct passwd *getpwResult;
|
||||
char *getpwBuffer;
|
||||
long getpwBufferSize;
|
||||
int getpwStatus;
|
||||
|
||||
getpwBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
if (getpwBufferSize == -1) {
|
||||
getpwBufferSize = 16384;
|
||||
}
|
||||
|
||||
getpwBuffer = new char[getpwBufferSize];
|
||||
if (getpwBuffer == nullptr) {
|
||||
return;
|
||||
}
|
||||
getpwStatus = getpwuid_r(uid, &passwdStruct, getpwBuffer, getpwBufferSize, &getpwResult);
|
||||
if ((getpwStatus == 0) && (getpwResult != nullptr)) {
|
||||
setUserName(QLatin1String(passwdStruct.pw_name));
|
||||
} else {
|
||||
setUserName(QString());
|
||||
qWarning() << "getpwuid_r returned error : " << getpwStatus;
|
||||
}
|
||||
delete[] getpwBuffer;
|
||||
}
|
||||
|
||||
bool UnixProcessInfo::readArguments(int pid)
|
||||
{
|
||||
// used for LinuxProcessInfo and SolarisProcessInfo
|
||||
// read command-line arguments file found at /proc/<pid>/cmdline
|
||||
// the expected format is a list of strings delimited by null characters,
|
||||
// and ending in a double null character pair.
|
||||
|
||||
QFile argumentsFile(QStringLiteral("/proc/%1/cmdline").arg(pid));
|
||||
if (argumentsFile.open(QIODevice::ReadOnly)) {
|
||||
QTextStream stream(&argumentsFile);
|
||||
const QString &data = stream.readAll();
|
||||
|
||||
const QStringList &argList = data.split(QLatin1Char('\0'));
|
||||
|
||||
for (const QString &entry : argList) {
|
||||
if (!entry.isEmpty()) {
|
||||
addArgument(entry);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setFileError(argumentsFile.error());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_FREEBSD) || defined(Q_OS_OPEN_BSD) || defined(Q_OS_MACOS)
|
||||
QSharedPointer<struct kinfo_proc> UnixProcessInfo::getProcInfoStruct(int *managementInfoBase, int mibCount)
|
||||
{
|
||||
size_t structLength;
|
||||
|
||||
if (::sysctl(managementInfoBase, mibCount, NULL, &structLength, NULL, 0) == -1) {
|
||||
qWarning() << "first sysctl() call failed with code " << errno;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QSharedPointer<struct kinfo_proc> kInfoProc(new struct kinfo_proc[structLength]);
|
||||
|
||||
if (::sysctl(managementInfoBase, mibCount, kInfoProc.get(), &structLength, NULL, 0) == -1) {
|
||||
qWarning() << "second sysctl() call failed with code " << errno;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return kInfoProc;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // Q_OS_WIN
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef UNIXPROCESSINFO_H
|
||||
#define UNIXPROCESSINFO_H
|
||||
|
||||
#include "ProcessInfo.h"
|
||||
|
||||
#if defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
#include <QSharedPointer>
|
||||
#include <sys/sysctl.h>
|
||||
#if defined(Q_OS_FREEBSD) || defined(Q_OS_OPEN_BSD)
|
||||
#include <sys/user.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
#ifndef Q_OS_WIN
|
||||
/**
|
||||
* Implementation of ProcessInfo for Unix platforms which uses
|
||||
* the /proc filesystem
|
||||
*/
|
||||
class UnixProcessInfo : public ProcessInfo
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructs a new instance of UnixProcessInfo.
|
||||
* See ProcessInfo::newInstance()
|
||||
*/
|
||||
explicit UnixProcessInfo(int pid);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Implementation of ProcessInfo::readProcessInfo(); calls the
|
||||
* four private methods below in turn.
|
||||
*/
|
||||
void readProcessInfo(int pid) override;
|
||||
|
||||
void readUserName(void) override;
|
||||
|
||||
bool readArguments(int pid) override;
|
||||
|
||||
#if defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
/**
|
||||
* Allocates an array of struct kinfo_proc and calls sysctl internally to fill it up
|
||||
* @param managementInfoBase management information base to pass to sysctl
|
||||
* @param mibCount number of elements in managementInfoBase, also passed as argument to sysctl
|
||||
* @return smart pointer to struct kinfo_proc[] and returns nullptr on failure
|
||||
*/
|
||||
QSharedPointer<struct kinfo_proc> getProcInfoStruct(int *managementInfoBase, int mibCount);
|
||||
#endif
|
||||
|
||||
private:
|
||||
/**
|
||||
* Read the standard process information -- PID, parent PID, foreground PID.
|
||||
* @param pid process ID to use
|
||||
* @return true on success
|
||||
*/
|
||||
virtual bool readProcInfo(int pid) = 0;
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef VIEWMANAGER_H
|
||||
#define VIEWMANAGER_H
|
||||
|
||||
// Qt
|
||||
#include <QAction>
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
|
||||
#include "konsoleprivate_export.h"
|
||||
// Konsole
|
||||
|
||||
class KActionCollection;
|
||||
class KConfigGroup;
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class ColorScheme;
|
||||
class Profile;
|
||||
class Session;
|
||||
class SessionController;
|
||||
class TabbedViewContainer;
|
||||
class TabbedViewContainer;
|
||||
class TerminalDisplay;
|
||||
class ViewProperties;
|
||||
class ViewSplitter;
|
||||
|
||||
/**
|
||||
* Manages the terminal display widgets in a Konsole window or part.
|
||||
*
|
||||
* When a view manager is created, it constructs a tab widget ( accessed via
|
||||
* widget() ) to hold one or more view splitters. Each view splitter holds
|
||||
* one or more terminal displays and splitters.
|
||||
*
|
||||
* The view manager provides menu actions ( defined in the 'konsoleui.rc' XML file )
|
||||
* to manipulate the views and view containers - for example, actions to split the view
|
||||
* left/right or top/bottom, detach a view from the current window and navigate between
|
||||
* views and containers. These actions are added to the collection specified in the
|
||||
* ViewManager's constructor.
|
||||
*
|
||||
* The view manager provides facilities to construct display widgets for a terminal
|
||||
* session and also to construct the SessionController which provides the menus and other
|
||||
* user interface elements specific to each display/session pair.
|
||||
*
|
||||
*/
|
||||
class KONSOLEPRIVATE_EXPORT ViewManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.kde.konsole.Window")
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a new view manager with the specified @p parent.
|
||||
* View-related actions defined in 'konsoleui.rc' are created
|
||||
* and added to the specified @p collection.
|
||||
*/
|
||||
ViewManager(QObject *parent, KActionCollection *collection);
|
||||
~ViewManager() override;
|
||||
|
||||
/**
|
||||
* Creates a new view to display the output from and deliver input to @p session.
|
||||
* Constructs a new container to hold the views if no container has yet been created.
|
||||
*/
|
||||
void createView(TabbedViewContainer *tabWidget, Session *session);
|
||||
|
||||
/*
|
||||
* Applies the view-specific settings associated with specified @p profile
|
||||
* to the terminal display @p view.
|
||||
*/
|
||||
void applyProfileToView(TerminalDisplay *view, const QExplicitlySharedDataPointer<Profile> &profile);
|
||||
|
||||
void toggleActionsBasedOnState();
|
||||
/**
|
||||
* Return the main widget for the view manager which
|
||||
* holds all of the views managed by this ViewManager instance.
|
||||
*/
|
||||
QWidget *widget() const;
|
||||
|
||||
/**
|
||||
* Returns the view manager's active view.
|
||||
*/
|
||||
QWidget *activeView() const;
|
||||
|
||||
/**
|
||||
* Returns the list of view properties for views in the active container.
|
||||
* Each view widget is associated with a ViewProperties instance which
|
||||
* provides access to basic information about the session being
|
||||
* displayed in the view, such as title, current directory and
|
||||
* associated icon.
|
||||
*/
|
||||
QList<ViewProperties *> viewProperties() const;
|
||||
|
||||
/**
|
||||
* This enum describes the available types of navigation widget
|
||||
* which newly created containers can provide to allow navigation
|
||||
* between open sessions.
|
||||
*/
|
||||
enum NavigationMethod {
|
||||
/**
|
||||
* Each container has a row of tabs (one per session) which the user
|
||||
* can click on to navigate between open sessions.
|
||||
*/
|
||||
TabbedNavigation,
|
||||
/** The container has no navigation widget. */
|
||||
NoNavigation,
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the options for showing or hiding the container's navigation widget.
|
||||
*/
|
||||
enum NavigationVisibility {
|
||||
NavigationNotSet, // Don't rely on this information, Only use the settings.
|
||||
AlwaysShowNavigation,
|
||||
ShowNavigationAsNeeded,
|
||||
AlwaysHideNavigation,
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the visibility of the view container's navigation widget.
|
||||
* The ViewContainer subclass is responsible for ensuring that this
|
||||
* setting is respected as views are dded or removed from the container
|
||||
*/
|
||||
void setNavigationVisibility(NavigationVisibility navigationVisibility);
|
||||
|
||||
/** Returns the current mode for controlling the visibility of the
|
||||
* view container's navigation widget.
|
||||
*/
|
||||
NavigationVisibility navigationVisibility() const;
|
||||
|
||||
/**
|
||||
* Sets the type of widget provided to navigate between open sessions
|
||||
* in a container. The changes will only apply to newly created containers.
|
||||
*
|
||||
* The default method is TabbedNavigation. To disable navigation widgets, call
|
||||
* setNavigationMethod(ViewManager::NoNavigation) before creating any sessions.
|
||||
*/
|
||||
void setNavigationMethod(NavigationMethod method);
|
||||
|
||||
/**
|
||||
* Returns the type of navigation widget created in new containers.
|
||||
* See setNavigationMethod()
|
||||
*/
|
||||
NavigationMethod navigationMethod() const;
|
||||
|
||||
/**
|
||||
* Returns the controller for the active view. activeViewChanged() is
|
||||
* emitted when this changes.
|
||||
*/
|
||||
SessionController *activeViewController() const;
|
||||
|
||||
/**
|
||||
* Session management
|
||||
*/
|
||||
void saveSessions(KConfigGroup &group);
|
||||
void restoreSessions(const KConfigGroup &group);
|
||||
|
||||
int managerId() const;
|
||||
|
||||
/** Returns a list of sessions in this ViewManager */
|
||||
QList<Session *> sessions()
|
||||
{
|
||||
return _sessionMap.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the @p profile has the blur setting enabled
|
||||
*/
|
||||
static bool profileHasBlurEnabled(const QExplicitlySharedDataPointer<Profile> &profile);
|
||||
|
||||
/** returns the active tab from the view
|
||||
*/
|
||||
TabbedViewContainer *activeContainer();
|
||||
TerminalDisplay *createView(Session *session);
|
||||
void attachView(TerminalDisplay *terminal, Session *session);
|
||||
|
||||
static std::shared_ptr<const ColorScheme> colorSchemeForProfile(const QExplicitlySharedDataPointer<Profile> &profile);
|
||||
/** Reorder the terminal display history list */
|
||||
void updateTerminalDisplayHistory(TerminalDisplay *terminalDisplay = nullptr, bool remove = false);
|
||||
|
||||
QHash<TerminalDisplay *, Session *> forgetAll(ViewSplitter *splitter);
|
||||
Session *forgetTerminal(TerminalDisplay *terminal);
|
||||
|
||||
/**
|
||||
* Creates and returns new session
|
||||
*
|
||||
* The session has specified @p profile, working @p directory
|
||||
* and configured environment.
|
||||
*/
|
||||
Session *createSession(const QExplicitlySharedDataPointer<Profile> &profile, const QString &directory = QString());
|
||||
|
||||
Q_SIGNALS:
|
||||
/** Emitted when the last view is removed from the view manager */
|
||||
void empty();
|
||||
|
||||
/** Emitted when a session is detached from a view owned by this ViewManager */
|
||||
void terminalsDetached(ViewSplitter *splitter, QHash<TerminalDisplay *, Session *> sessionsMap);
|
||||
|
||||
/**
|
||||
* Emitted when the active view changes.
|
||||
* @param controller The controller associated with the active view
|
||||
*/
|
||||
void activeViewChanged(SessionController *controller);
|
||||
|
||||
/**
|
||||
* Emitted when the current session needs unplugged from factory().
|
||||
* @param controller The controller associated with the active view
|
||||
*/
|
||||
void unplugController(SessionController *controller);
|
||||
|
||||
/**
|
||||
* Emitted when the list of view properties ( as returned by viewProperties() ) changes.
|
||||
* This occurs when views are added to or removed from the active container, or
|
||||
* if the active container is changed.
|
||||
*/
|
||||
void viewPropertiesChanged(const QList<ViewProperties *> &propertiesList);
|
||||
|
||||
/**
|
||||
* Emitted when menu bar visibility changes because a profile that requires so is
|
||||
* activated.
|
||||
*/
|
||||
void setMenuBarVisibleRequest(bool);
|
||||
void updateWindowIcon();
|
||||
|
||||
void blurSettingChanged(bool);
|
||||
|
||||
/** Requests creation of a new view with the default profile. */
|
||||
void newViewRequest();
|
||||
/** Requests creation of a new view, with the selected profile. */
|
||||
void newViewWithProfileRequest(const QExplicitlySharedDataPointer<Profile> &profile);
|
||||
|
||||
void activationRequest(const QString &xdgActivationToken);
|
||||
|
||||
public Q_SLOTS:
|
||||
/** DBus slot that returns the number of sessions in the current view. */
|
||||
Q_SCRIPTABLE int sessionCount();
|
||||
|
||||
/**
|
||||
* DBus slot that returns the unique ids of the sessions in the
|
||||
* current view. The returned list is ordered by tab.
|
||||
* QList<int> is not printable by qdbus so we use QStringList
|
||||
* Example:
|
||||
* A) create tab, create tab 2, create tab 3, go to tab 2, split view
|
||||
* sessionList() returns 1 4 2 3
|
||||
* B) create tab, create tab 2, split view, create tab 3
|
||||
* sessionList() returns 1 3 2 4
|
||||
*/
|
||||
Q_SCRIPTABLE QStringList sessionList();
|
||||
|
||||
/** DBus slot that returns the current (active) session window */
|
||||
Q_SCRIPTABLE int currentSession();
|
||||
|
||||
/** DBus slot that sets the current (active) session window */
|
||||
Q_SCRIPTABLE void setCurrentSession(int sessionId);
|
||||
|
||||
/** DBus slot that creates a new session in the current view with the associated
|
||||
* default profile and the default working directory
|
||||
*/
|
||||
Q_SCRIPTABLE int newSession();
|
||||
|
||||
/** DBus slot that creates a new session in the current view.
|
||||
* @param profile the name of the profile to be used
|
||||
* started.
|
||||
*/
|
||||
Q_SCRIPTABLE int newSession(const QString &profile);
|
||||
|
||||
/** DBus slot that creates a new session in the current view.
|
||||
* @param profile the name of the profile to be used
|
||||
* @param directory the working directory where the session is
|
||||
* started.
|
||||
*/
|
||||
Q_SCRIPTABLE int newSession(const QString &profile, const QString &directory);
|
||||
|
||||
// TODO: its semantic is application-wide. Move it to more appropriate place
|
||||
// DBus slot that returns the name of default profile
|
||||
Q_SCRIPTABLE QString defaultProfile();
|
||||
|
||||
// TODO: its semantic is application-wide. Move it to more appropriate place
|
||||
// DBus slot that sets the default profile
|
||||
Q_SCRIPTABLE void setDefaultProfile(const QString &profile);
|
||||
|
||||
// TODO: its semantic is application-wide. Move it to more appropriate place
|
||||
// DBus slot that returns a string list of defined (known) profiles
|
||||
Q_SCRIPTABLE QStringList profileList();
|
||||
|
||||
/** DBus slot that changes the view port to the next session */
|
||||
Q_SCRIPTABLE void nextSession();
|
||||
|
||||
/** DBus slot that changes the view port to the previous session */
|
||||
Q_SCRIPTABLE void prevSession();
|
||||
|
||||
/** DBus slot that switches the current session (as returned by
|
||||
* currentSession()) with the left (or previous) one in the
|
||||
* navigation tab.
|
||||
*/
|
||||
Q_SCRIPTABLE void moveSessionLeft();
|
||||
|
||||
/** DBus slot that Switches the current session (as returned by
|
||||
* currentSession()) with the right (or next) one in the navigation
|
||||
* tab.
|
||||
*/
|
||||
Q_SCRIPTABLE void moveSessionRight();
|
||||
|
||||
/** DBus slot that sets ALL tabs' width to match their text */
|
||||
Q_SCRIPTABLE void setTabWidthToText(bool);
|
||||
|
||||
// Creates json file with split config
|
||||
Q_SCRIPTABLE void saveLayoutFile();
|
||||
Q_SCRIPTABLE void loadLayoutFile();
|
||||
Q_SCRIPTABLE void loadLayout(QString File);
|
||||
|
||||
/** DBus slot that returns a description of the layout hierarchy
|
||||
* in each tab.
|
||||
*
|
||||
* A ViewSplitter is described by its id enclosed in round brackets,
|
||||
* followed by square (horizontal split) or curly (vertical split)
|
||||
* brackets. Format: (%id)[...] OR (%id){...}
|
||||
*
|
||||
* A TerminalDisplay is simply described by its id.
|
||||
*
|
||||
* The child widgets of a ViewSplitter are described in the square or
|
||||
* curly brackets using their ids and are separated by '|'. The
|
||||
* order which they are described in is top to bottom for vertical
|
||||
* split or left to right for horizontal split.
|
||||
*/
|
||||
Q_SCRIPTABLE QStringList viewHierarchy();
|
||||
|
||||
/** DBus slot that returns the sizes of the direct child widgets as
|
||||
* percentages of the size of the ViewSplitter in the form of a
|
||||
* list. Will return an empty list if a splitter with id splitterId
|
||||
* cannot be found.
|
||||
*/
|
||||
Q_SCRIPTABLE QList<double> getSplitProportions(int splitterId);
|
||||
|
||||
/** DBus slot that splits an existing view in the current tab **/
|
||||
Q_SCRIPTABLE bool createSplit(int viewId, bool horizontalSplit);
|
||||
|
||||
/** DBus slot that creates a splitter containing a series of
|
||||
* existing widgets and inserts it at an index in an existing splitter.
|
||||
*
|
||||
* Each element in widgetInfos is QString of format "x-y", describing
|
||||
* each moved widget. Regarding the to-be-created splitter, the first
|
||||
* QString will represent the leftmost widget (horizontal split)
|
||||
* or the topmost widget (vertical split). x is either 's'
|
||||
* for a ViewSplitter or 'v' for a TerminalDisplay. y is the id of
|
||||
* the widget.
|
||||
*/
|
||||
Q_SCRIPTABLE bool createSplitWithExisting(int targetSplitterId, QStringList widgetInfos, int idx, bool horizontalSplit);
|
||||
|
||||
/** DBus slot that focuses a view. **/
|
||||
Q_SCRIPTABLE bool setCurrentView(int viewId);
|
||||
|
||||
/** DBus slot that allows setting of the relative sizes of widgets
|
||||
* in a splitter by specifying their percentages.
|
||||
*/
|
||||
/* NOTE: not all ways of calling dbus can handle "ad" QList<double>
|
||||
Does not work AFAIK: qdbus6, qdbusviewer6, busctl
|
||||
Works: dbus-send, gdbus
|
||||
Example:
|
||||
dbus-send --session --print-reply=literal --type=method_call --dest=org.kde.konsole-16710 /Windows/1 org.kde.konsole.Window.resizeSplits int32:0 array:double:40.0,60.0
|
||||
gdbus call --session --dest org.kde.konsole-16710 --object-path /Windows/1 --method org.kde.konsole.Window.resizeSplits 0 "[10.5, 89.5]"
|
||||
*/
|
||||
Q_SCRIPTABLE bool resizeSplits(int splitterId, QList<double> percentages);
|
||||
|
||||
/** DBus slot that moves a splitter to a different position under a specified splitter**/
|
||||
Q_SCRIPTABLE bool moveSplitter(int splitterId, int targetSplitterId, int idx);
|
||||
|
||||
/** DBus slot that move a view to a different position under a specified splitter**/
|
||||
Q_SCRIPTABLE bool moveView(int viewId, int targetSplitterId, int idx);
|
||||
|
||||
private Q_SLOTS:
|
||||
// called when the "Split View Left/Right" menu item is selected
|
||||
void splitLeftRight();
|
||||
void splitTopBottom();
|
||||
void splitAuto(bool fromNextTab = false);
|
||||
void splitLeftRightNextTab();
|
||||
void splitTopBottomNextTab();
|
||||
void splitAutoNextTab();
|
||||
void expandActiveContainer();
|
||||
void shrinkActiveContainer();
|
||||
void equalSizeAllContainers();
|
||||
|
||||
// called when the "Detach View" menu item is selected
|
||||
void detachActiveView();
|
||||
void detachActiveTab();
|
||||
|
||||
// called when a session terminates - the view manager will delete any
|
||||
// views associated with the session
|
||||
void sessionFinished(Session *session);
|
||||
// called when one view has been destroyed
|
||||
void viewDestroyed(QWidget *view);
|
||||
|
||||
// controller detects when an associated view is given the focus
|
||||
// and emits a signal. ViewManager listens for that signal
|
||||
// and then plugs the action into the UI
|
||||
// void viewFocused( SessionController* controller );
|
||||
|
||||
// called when the active view in a ViewContainer changes, so
|
||||
// that we can plug the appropriate actions into the UI
|
||||
void activateView(TerminalDisplay *view);
|
||||
|
||||
void focusUp();
|
||||
void focusDown();
|
||||
void focusLeft();
|
||||
void focusRight();
|
||||
|
||||
// called when "Next View" shortcut is activated
|
||||
void nextView();
|
||||
|
||||
// called when "Previous View" shortcut is activated
|
||||
void previousView();
|
||||
|
||||
// called when "Switch to last tab" shortcut is activated
|
||||
void lastView();
|
||||
|
||||
// called when "Switch to last used tab" shortcut is activated
|
||||
void lastUsedView();
|
||||
|
||||
// called when "Switch to last used tab (reverse)" shortcut is activated
|
||||
void lastUsedViewReverse();
|
||||
|
||||
// called when "Next View Container" shortcut is activated
|
||||
void nextContainer();
|
||||
|
||||
// called when "Toggle Two tabs" shortcut is activated
|
||||
void toggleTwoViews();
|
||||
|
||||
// called when the views in a container owned by this view manager
|
||||
// changes
|
||||
void containerViewsChanged(TabbedViewContainer *container);
|
||||
|
||||
// called when a profile changes
|
||||
void profileChanged(const QExplicitlySharedDataPointer<Profile> &profile);
|
||||
|
||||
void updateViewsForSession(Session *session);
|
||||
|
||||
// moves active view to the left
|
||||
void moveActiveViewLeft();
|
||||
// moves active view to the right
|
||||
void moveActiveViewRight();
|
||||
// switches to the view at visual position 'index'
|
||||
// in the current container
|
||||
void switchToView(int index);
|
||||
// gives focus and switches the terminal display, changing tab if needed
|
||||
void switchToTerminalDisplay(TerminalDisplay *terminalDisplay);
|
||||
|
||||
// called when a SessionController gains focus
|
||||
void controllerChanged(SessionController *controller);
|
||||
|
||||
/**
|
||||
* Disconnect this ViewManager and MainWindow from @p controller
|
||||
* and its associated view/session pair such as after a split-view
|
||||
* has been drag-and-dropped to a new window.
|
||||
*/
|
||||
void forgetController(SessionController *controller);
|
||||
|
||||
/* Detaches the tab at index tabIdx */
|
||||
void detachTab(int tabIdx);
|
||||
|
||||
void semanticSetupBash();
|
||||
|
||||
void toggleSemanticHints();
|
||||
|
||||
void toggleLineNumbers();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(ViewManager)
|
||||
|
||||
TerminalDisplay *findTerminalDisplay(int viewId);
|
||||
|
||||
void setCurrentView(TerminalDisplay *view);
|
||||
|
||||
void createView(Session *session, TabbedViewContainer *container, int index);
|
||||
|
||||
void setupActions();
|
||||
|
||||
// takes a view from a view container owned by a different manager and places it in
|
||||
// newContainer owned by this manager
|
||||
void takeView(ViewManager *otherManager, TabbedViewContainer *otherContainer, TabbedViewContainer *newContainer, TerminalDisplay *view);
|
||||
void splitView(Qt::Orientation orientation, bool fromNextTab = false);
|
||||
|
||||
// creates a new container which can hold terminal displays
|
||||
TabbedViewContainer *createContainer();
|
||||
|
||||
// creates a new terminal display
|
||||
// the 'session' is used so that the terminal display's random seed
|
||||
// can be set to something which depends uniquely on that session
|
||||
TerminalDisplay *createTerminalDisplay(Session *session = nullptr);
|
||||
|
||||
// creates a new controller for a session/display pair which provides the menu
|
||||
// actions associated with that view, and exposes basic information
|
||||
// about the session ( such as title and associated icon ) to the display.
|
||||
SessionController *createController(Session *session, TerminalDisplay *view);
|
||||
void removeController(SessionController *controller);
|
||||
|
||||
// Activates a different terminal when the TerminalDisplay
|
||||
// closes or is detached and another one should be focused.
|
||||
// It will activate the last used terminal within the same splitView
|
||||
// if possible otherwise it will focus the last used tab
|
||||
void focusAnotherTerminal(ViewSplitter *toplevelSplitter);
|
||||
|
||||
void activateLastUsedView(bool reverse);
|
||||
|
||||
void registerTerminal(TerminalDisplay *terminal);
|
||||
void unregisterTerminal(TerminalDisplay *terminal);
|
||||
|
||||
private:
|
||||
QPointer<TabbedViewContainer> _viewContainer;
|
||||
QPointer<SessionController> _pluggedController;
|
||||
|
||||
QHash<TerminalDisplay *, Session *> _sessionMap;
|
||||
|
||||
KActionCollection *_actionCollection;
|
||||
|
||||
NavigationMethod _navigationMethod;
|
||||
NavigationVisibility _navigationVisibility;
|
||||
int _managerId;
|
||||
static int lastManagerId;
|
||||
QList<TerminalDisplay *> _terminalDisplayHistory;
|
||||
int _terminalDisplayHistoryIndex;
|
||||
|
||||
// List of actions that should only be enabled when there are multiple view
|
||||
// containers open
|
||||
QList<QAction *> _multiTabOnlyActions;
|
||||
QList<QAction *> _multiSplitterOnlyActions;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "ViewProperties.h"
|
||||
|
||||
using Konsole::ViewProperties;
|
||||
|
||||
QHash<int, ViewProperties *> ViewProperties::_viewProperties;
|
||||
|
||||
ViewProperties::ViewProperties(QObject *parent)
|
||||
: QObject(parent)
|
||||
, _icon(QIcon())
|
||||
, _title(QString())
|
||||
, _identifier(0)
|
||||
{
|
||||
}
|
||||
|
||||
ViewProperties::~ViewProperties()
|
||||
{
|
||||
_viewProperties.remove(_identifier);
|
||||
}
|
||||
|
||||
ViewProperties *ViewProperties::propertiesById(int id)
|
||||
{
|
||||
return _viewProperties[id];
|
||||
}
|
||||
|
||||
QUrl ViewProperties::url() const
|
||||
{
|
||||
return QUrl();
|
||||
}
|
||||
|
||||
QString ViewProperties::currentDir() const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
void ViewProperties::fireActivity()
|
||||
{
|
||||
Q_EMIT activity(this);
|
||||
}
|
||||
|
||||
void ViewProperties::rename()
|
||||
{
|
||||
}
|
||||
|
||||
void ViewProperties::setTitle(const QString &title)
|
||||
{
|
||||
if (title != _title) {
|
||||
_title = title;
|
||||
Q_EMIT titleChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewProperties::setIcon(const QIcon &icon)
|
||||
{
|
||||
// the icon's cache key is used to determine whether this icon is the same
|
||||
// as the old one. if so no signal is emitted.
|
||||
|
||||
if (icon.cacheKey() != _icon.cacheKey()) {
|
||||
_icon = icon;
|
||||
Q_EMIT iconChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewProperties::setColor(const QColor &color)
|
||||
{
|
||||
if (color != _color) {
|
||||
_color = color;
|
||||
Q_EMIT colorChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewProperties::setIdentifier(int id)
|
||||
{
|
||||
if (_viewProperties.contains(_identifier)) {
|
||||
_viewProperties.remove(_identifier);
|
||||
}
|
||||
|
||||
_identifier = id;
|
||||
|
||||
_viewProperties.insert(id, this);
|
||||
}
|
||||
|
||||
QString ViewProperties::title() const
|
||||
{
|
||||
return _title;
|
||||
}
|
||||
|
||||
QIcon ViewProperties::icon() const
|
||||
{
|
||||
return _icon;
|
||||
}
|
||||
|
||||
int ViewProperties::identifier() const
|
||||
{
|
||||
return _identifier;
|
||||
}
|
||||
|
||||
QColor ViewProperties::color() const
|
||||
{
|
||||
return _color;
|
||||
}
|
||||
|
||||
#include "moc_ViewProperties.cpp"
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef VIEWPROPERTIES_H
|
||||
#define VIEWPROPERTIES_H
|
||||
|
||||
// Qt
|
||||
#include <QColor>
|
||||
#include <QHash>
|
||||
#include <QIcon>
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
// Konsole
|
||||
#include "konsoleprivate_export.h"
|
||||
#include "session/Session.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
/**
|
||||
* Encapsulates user-visible information about the terminal session currently being displayed in a view,
|
||||
* such as the associated title and icon.
|
||||
*
|
||||
* This can be used by navigation widgets in a ViewContainer sub-class to provide a tab, label or other
|
||||
* item for switching between views.
|
||||
*/
|
||||
class KONSOLEPRIVATE_EXPORT ViewProperties : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ViewProperties(QObject *parent);
|
||||
~ViewProperties() override;
|
||||
|
||||
/** Returns the icon associated with a view */
|
||||
QIcon icon() const;
|
||||
/** Returns the title associated with a view */
|
||||
QString title() const;
|
||||
/** Returns the color associated with a view */
|
||||
QColor color() const;
|
||||
|
||||
/**
|
||||
* Returns the URL current associated with a view.
|
||||
* The default implementation returns an empty URL.
|
||||
*/
|
||||
virtual QUrl url() const;
|
||||
|
||||
/**
|
||||
* Returns the current directory associated with a view.
|
||||
* This may be the same as url()
|
||||
* The default implementation returns an empty string.
|
||||
*/
|
||||
virtual QString currentDir() const;
|
||||
|
||||
/**
|
||||
* A unique identifier associated with this
|
||||
* ViewProperties instance.
|
||||
*/
|
||||
int identifier() const;
|
||||
|
||||
/**
|
||||
* Sub-classes may re-implement this method to display a message to the user
|
||||
* to allow them to confirm whether to close a view.
|
||||
* The default implementation always returns true
|
||||
*/
|
||||
virtual bool confirmClose() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Finds a ViewProperties instance given its numeric identifier. */
|
||||
static ViewProperties *propertiesById(int id);
|
||||
|
||||
Q_SIGNALS:
|
||||
/** Emitted when the icon for a view changes */
|
||||
void iconChanged(ViewProperties *properties);
|
||||
/** Emitted when the title for a view changes */
|
||||
void titleChanged(ViewProperties *properties);
|
||||
/** Emitted when the color for a view changes */
|
||||
void colorChanged(ViewProperties *properties);
|
||||
/** Emitted when activity has occurred in this view. */
|
||||
void activity(ViewProperties *item);
|
||||
/** Emitted when notification for a view changes */
|
||||
void notificationChanged(ViewProperties *item, Session::Notification notification, bool enabled);
|
||||
/** Emitted when read only state changes */
|
||||
void readOnlyChanged(ViewProperties *item);
|
||||
/** Emitted when "copy input" state changes */
|
||||
void copyInputChanged(ViewProperties *item);
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Requests the renaming of this view.
|
||||
* The default implementation does nothing.
|
||||
*/
|
||||
virtual void rename();
|
||||
|
||||
protected Q_SLOTS:
|
||||
/** Emits the activity() signal. */
|
||||
void fireActivity();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Subclasses may call this method to change the title. This causes
|
||||
* a titleChanged() signal to be emitted
|
||||
*/
|
||||
void setTitle(const QString &title);
|
||||
/**
|
||||
* Subclasses may call this method to change the icon. This causes
|
||||
* an iconChanged() signal to be emitted
|
||||
*/
|
||||
void setIcon(const QIcon &icon);
|
||||
/**
|
||||
* Subclasses may call this method to change the color. This causes
|
||||
* a colorChanged() signal to be emitted
|
||||
*/
|
||||
void setColor(const QColor &color);
|
||||
/** Subclasses may call this method to change the identifier. */
|
||||
void setIdentifier(int id);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(ViewProperties)
|
||||
|
||||
QIcon _icon;
|
||||
QString _title;
|
||||
QColor _color;
|
||||
int _identifier;
|
||||
|
||||
static QHash<int, ViewProperties *> _viewProperties;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // VIEWPROPERTIES_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
||||
SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef VT102EMULATION_H
|
||||
#define VT102EMULATION_H
|
||||
|
||||
// Qt
|
||||
#include <QHash>
|
||||
#include <QMap>
|
||||
#include <QMediaPlayer>
|
||||
#include <QPair>
|
||||
#include <QVector>
|
||||
#include <QList>
|
||||
|
||||
|
||||
// Konsole
|
||||
#include "Emulation.h"
|
||||
#include "Screen.h"
|
||||
#include "keyboardtranslator/KeyboardTranslator.h"
|
||||
|
||||
class QTimer;
|
||||
class QKeyEvent;
|
||||
|
||||
#define MODE_AppCuKeys (MODES_SCREEN + 0) // Application cursor keys (DECCKM)
|
||||
#define MODE_AppKeyPad (MODES_SCREEN + 1) //
|
||||
#define MODE_Mouse1000 (MODES_SCREEN + 2) // Send mouse X,Y position on press and release
|
||||
#define MODE_Mouse1001 (MODES_SCREEN + 3) // Use Highlight mouse tracking
|
||||
#define MODE_Mouse1002 (MODES_SCREEN + 4) // Use cell motion mouse tracking
|
||||
#define MODE_Mouse1003 (MODES_SCREEN + 5) // Use all motion mouse tracking
|
||||
#define MODE_Mouse1005 (MODES_SCREEN + 6) // Xterm-style extended coordinates
|
||||
#define MODE_Mouse1006 (MODES_SCREEN + 7) // 2nd Xterm-style extended coordinates
|
||||
#define MODE_Mouse1007 (MODES_SCREEN + 8) // XTerm Alternate Scroll mode; also check AlternateScrolling profile property
|
||||
#define MODE_Mouse1015 (MODES_SCREEN + 9) // Urxvt-style extended coordinates
|
||||
#define MODE_Ansi (MODES_SCREEN + 10) // Use US Ascii for character sets G0-G3 (DECANM)
|
||||
#define MODE_132Columns (MODES_SCREEN + 11) // 80 <-> 132 column mode switch (DECCOLM)
|
||||
#define MODE_Allow132Columns (MODES_SCREEN + 12) // Allow DECCOLM mode
|
||||
#define MODE_BracketedPaste (MODES_SCREEN + 13) // Xterm-style bracketed paste mode
|
||||
#define MODE_Sixel (MODES_SCREEN + 14) // Xterm-style bracketed paste mode
|
||||
#define MODE_total (MODES_SCREEN + 15)
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
extern unsigned short vt100_graphics[32];
|
||||
|
||||
struct CharCodes {
|
||||
// coding info
|
||||
char charset[4]; //
|
||||
int cu_cs; // actual charset.
|
||||
bool graphic; // Some VT100 tricks
|
||||
bool pound; // Some VT100 tricks
|
||||
bool sa_graphic; // saved graphic
|
||||
bool sa_pound; // saved pound
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides an xterm compatible terminal emulation based on the DEC VT102 terminal.
|
||||
* A full description of this terminal can be found at https://vt100.net/docs/vt102-ug/
|
||||
*
|
||||
* In addition, various additional xterm escape sequences are supported to provide
|
||||
* features such as mouse input handling.
|
||||
* See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html for a description of xterm's escape
|
||||
* sequences.
|
||||
*
|
||||
*/
|
||||
class KONSOLEPRIVATE_EXPORT Vt102Emulation : public Emulation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/** Constructs a new emulation */
|
||||
Vt102Emulation();
|
||||
~Vt102Emulation() override;
|
||||
|
||||
// reimplemented from Emulation
|
||||
void clearEntireScreen() override;
|
||||
void reset(bool softReset = false, bool preservePrompt = false) override;
|
||||
char eraseChar() const override;
|
||||
|
||||
public Q_SLOTS:
|
||||
// reimplemented from Emulation
|
||||
void sendString(const QByteArray &string) override;
|
||||
void sendText(const QString &text) override;
|
||||
void sendKeyEvent(QKeyEvent *) override;
|
||||
void sendMouseEvent(int buttons, int column, int line, int eventType) override;
|
||||
void focusChanged(bool focused) override;
|
||||
void clearHistory() override;
|
||||
|
||||
protected:
|
||||
// reimplemented from Emulation
|
||||
void setMode(int mode) override;
|
||||
void resetMode(int mode) override;
|
||||
void receiveChars(const QVector<uint> &chars) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
// Causes sessionAttributeChanged() to be emitted for each (int,QString)
|
||||
// pair in _pendingSessionAttributesUpdates.
|
||||
// Used to buffer multiple attribute updates in the current session
|
||||
void updateSessionAttributes();
|
||||
void deletePlayer(QMediaPlayer::MediaStatus);
|
||||
|
||||
private:
|
||||
unsigned int applyCharset(uint c);
|
||||
void setCharset(int n, int cs);
|
||||
void useCharset(int n);
|
||||
void setAndUseCharset(int n, int cs);
|
||||
void saveCursor();
|
||||
void restoreCursor();
|
||||
void resetCharset(int scrno);
|
||||
|
||||
void setMargins(int top, int bottom);
|
||||
// set margins for all screens back to their defaults
|
||||
void setDefaultMargins();
|
||||
|
||||
// returns true if 'mode' is set or false otherwise
|
||||
bool getMode(int mode);
|
||||
// saves the current boolean value of 'mode'
|
||||
void saveMode(int mode);
|
||||
// restores the boolean value of 'mode'
|
||||
void restoreMode(int mode);
|
||||
// resets all modes
|
||||
// (except MODE_Allow132Columns)
|
||||
void resetModes();
|
||||
|
||||
void resetTokenizer();
|
||||
#define MAX_TOKEN_LENGTH 256 // Max length of tokens (e.g. window title)
|
||||
void addToCurrentToken(uint cc);
|
||||
int tokenBufferPos;
|
||||
|
||||
protected:
|
||||
QList<char32_t> tokenBuffer;
|
||||
|
||||
private:
|
||||
#define MAXARGS 16
|
||||
void addDigit(int dig);
|
||||
void addArgument();
|
||||
void addSub();
|
||||
|
||||
struct subParam {
|
||||
int value[MAXARGS]; // value[0] unused, it would correspond to the containing param value
|
||||
int count;
|
||||
};
|
||||
|
||||
struct {
|
||||
int value[MAXARGS];
|
||||
struct subParam sub[MAXARGS];
|
||||
int count;
|
||||
bool hasSubParams;
|
||||
} params = {};
|
||||
|
||||
void initTokenizer();
|
||||
|
||||
enum ParserStates {
|
||||
Ground,
|
||||
Escape,
|
||||
EscapeIntermediate,
|
||||
CsiEntry,
|
||||
CsiParam,
|
||||
CsiIntermediate,
|
||||
CsiIgnore,
|
||||
DcsEntry,
|
||||
DcsParam,
|
||||
DcsIntermediate,
|
||||
DcsPassthrough,
|
||||
DcsIgnore,
|
||||
OscString,
|
||||
SosPmApcString,
|
||||
|
||||
Vt52Escape,
|
||||
Vt52CupRow,
|
||||
Vt52CupColumn,
|
||||
};
|
||||
|
||||
enum {
|
||||
Sos,
|
||||
Pm,
|
||||
Apc,
|
||||
} _sosPmApc;
|
||||
|
||||
enum osc {
|
||||
// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands
|
||||
ReportColors = 4,
|
||||
ResetColors = 104,
|
||||
// https://gitlab.freedesktop.org/Per_Bothner/specifications/blob/master/proposals/semantic-prompts.md
|
||||
SemanticPrompts = 133,
|
||||
// https://chromium.googlesource.com/apps/libapps/+/master/hterm/doc/ControlSequences.md#OSC
|
||||
Notification = 777,
|
||||
Image = 1337,
|
||||
};
|
||||
|
||||
ParserStates _state = Ground;
|
||||
bool _ignore = false;
|
||||
int _nIntermediate = 0;
|
||||
unsigned char _intermediate[1];
|
||||
// Used to get expected behaviour in emulated up/down movement in REPL mode.
|
||||
int _targetCol = -1;
|
||||
|
||||
void switchState(const ParserStates newState, const uint cc);
|
||||
void esc_dispatch(const uint cc);
|
||||
void clear();
|
||||
void collect(const uint cc);
|
||||
void param(const uint cc);
|
||||
void csi_dispatch(const uint cc);
|
||||
void osc_start();
|
||||
void osc_put(const uint cc);
|
||||
void osc_end(const uint cc);
|
||||
void hook(const uint cc);
|
||||
void unhook();
|
||||
void put(const uint cc);
|
||||
void apc_start(const uint cc);
|
||||
void apc_put(const uint cc);
|
||||
void apc_end();
|
||||
|
||||
// State machine for escape sequences containing large amount of data
|
||||
int tokenState;
|
||||
const char *tokenStateChange;
|
||||
int tokenPos;
|
||||
QByteArray tokenData;
|
||||
|
||||
// Set of flags for each of the ASCII characters which indicates
|
||||
// what category they fall into (printable character, control, digit etc.)
|
||||
// for the purposes of decoding terminal output
|
||||
int charClass[256];
|
||||
|
||||
QByteArray imageData;
|
||||
quint32 imageId;
|
||||
QMap<char, qint64> savedKeys;
|
||||
|
||||
protected:
|
||||
virtual void reportDecodingError(int token);
|
||||
|
||||
virtual void processToken(int code, int p, int q);
|
||||
virtual void processSessionAttributeRequest(const int tokenSize, const uint terminator);
|
||||
virtual void processChecksumRequest(int argc, int argv[]);
|
||||
|
||||
private:
|
||||
void processGraphicsToken(int tokenSize);
|
||||
|
||||
void sendGraphicsReply(const QString ¶ms, const QString &error);
|
||||
void reportTerminalType();
|
||||
void reportTertiaryAttributes();
|
||||
void reportSecondaryAttributes();
|
||||
void reportVersion();
|
||||
void reportStatus();
|
||||
void reportAnswerBack();
|
||||
void reportCursorPosition();
|
||||
void reportPixelSize();
|
||||
void reportCellSize();
|
||||
void iTermReportCellSize();
|
||||
void reportSize();
|
||||
void reportColor(int c, QColor color);
|
||||
void reportTerminalParms(int p);
|
||||
|
||||
void emulateUpDown(int up, KeyboardTranslator::Entry entry, QByteArray &textToSend, int toCol = -1);
|
||||
|
||||
// clears the screen and resizes it to the specified
|
||||
// number of columns
|
||||
void clearScreenAndSetColumns(int columnCount);
|
||||
|
||||
CharCodes _charset[2];
|
||||
|
||||
class TerminalState
|
||||
{
|
||||
public:
|
||||
// Initializes all modes to false
|
||||
TerminalState()
|
||||
{
|
||||
memset(&mode, 0, MODE_total * sizeof(bool));
|
||||
}
|
||||
|
||||
bool mode[MODE_total];
|
||||
};
|
||||
|
||||
TerminalState _currentModes;
|
||||
TerminalState _savedModes;
|
||||
|
||||
// Hash table and timer for buffering calls to update certain session
|
||||
// attributes (e.g. the name of the session, window title).
|
||||
// These calls occur when certain escape sequences are detected in the
|
||||
// output from the terminal. See Emulation::sessionAttributeChanged()
|
||||
QHash<int, QString> _pendingSessionAttributesUpdates;
|
||||
QTimer *_sessionAttributesUpdateTimer;
|
||||
|
||||
bool _reportFocusEvents;
|
||||
|
||||
QColor colorTable[256];
|
||||
|
||||
// Sixel:
|
||||
#define MAX_SIXEL_COLORS 256
|
||||
#define MAX_IMAGE_DIM 16384
|
||||
void sixelQuery(int query);
|
||||
bool processSixel(uint cc);
|
||||
void SixelModeEnable(int width, int height /*, bool preserveBackground*/);
|
||||
void SixelModeAbort();
|
||||
void SixelModeDisable();
|
||||
void SixelColorChangeRGB(const int index, int red, int green, int blue);
|
||||
void SixelColorChangeHSL(const int index, int hue, int saturation, int value);
|
||||
void SixelCharacterAdd(uint8_t character, int repeat = 1);
|
||||
bool m_SixelPictureDefinition = false;
|
||||
bool m_SixelStarted = false;
|
||||
QImage m_currentImage;
|
||||
int m_currentX = 0;
|
||||
int m_verticalPosition = 0;
|
||||
uint8_t m_currentColor = 0;
|
||||
bool m_preserveBackground = true;
|
||||
QPair<int, int> m_aspect = qMakePair(1, 1);
|
||||
bool m_SixelScrolling = true;
|
||||
QSize m_actualSize; // For efficiency reasons, we keep the image in memory larger than what the end result is
|
||||
|
||||
// Kitty
|
||||
QHash<int, QPixmap> _graphicsImages;
|
||||
// For kitty graphics protocol - image cache
|
||||
int getFreeGraphicsImageId();
|
||||
|
||||
QMediaPlayer *player;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // VT102EMULATION_H
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2012 Jekyll Wu <adaptee@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "WindowSystemInfo.h"
|
||||
|
||||
#include "config-konsole.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#if WITH_X11
|
||||
#include <KWindowSystem>
|
||||
#include <KX11Extras>
|
||||
#endif
|
||||
|
||||
using Konsole::WindowSystemInfo;
|
||||
|
||||
bool WindowSystemInfo::HAVE_TRANSPARENCY = false;
|
||||
|
||||
bool WindowSystemInfo::compositingActive()
|
||||
{
|
||||
#if WITH_X11
|
||||
return !KWindowSystem::isPlatformX11() || KX11Extras::compositingActive();
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2012 Jekyll Wu <adaptee@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
#ifndef WINDOWSYSTEMINFO_H
|
||||
#define WINDOWSYSTEMINFO_H
|
||||
|
||||
#include "konsoleprivate_export.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class KONSOLEPRIVATE_EXPORT WindowSystemInfo
|
||||
{
|
||||
public:
|
||||
static bool HAVE_TRANSPARENCY;
|
||||
|
||||
static bool compositingActive();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // WINDOWSYSTEMINFO_H
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2002 Waldo Bastian <bastian@kde.org>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0-only
|
||||
**/
|
||||
|
||||
// Own
|
||||
#include "ZModemDialog.h"
|
||||
|
||||
// KDE
|
||||
#include <KTextEdit>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
using Konsole::ZModemDialog;
|
||||
|
||||
ZModemDialog::ZModemDialog(QWidget *aParent, bool modal, const QString &caption)
|
||||
: QDialog(aParent)
|
||||
, _textEdit(nullptr)
|
||||
, mButtonBox(nullptr)
|
||||
{
|
||||
setObjectName(QStringLiteral("zmodem_progress"));
|
||||
setModal(modal);
|
||||
setWindowTitle(caption);
|
||||
|
||||
mButtonBox = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Close);
|
||||
auto mainWidget = new QWidget(this);
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
setLayout(mainLayout);
|
||||
mainLayout->addWidget(mainWidget);
|
||||
mainLayout->addWidget(mButtonBox);
|
||||
|
||||
// Use Cancel here to stop the transfer
|
||||
mButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(true);
|
||||
mButtonBox->button(QDialogButtonBox::Close)->setEnabled(false);
|
||||
|
||||
connect(mButtonBox, &QDialogButtonBox::rejected, this, &Konsole::ZModemDialog::slotCancel);
|
||||
connect(mButtonBox, &QDialogButtonBox::accepted, this, &Konsole::ZModemDialog::slotClose);
|
||||
|
||||
_textEdit = new KTextEdit(this);
|
||||
_textEdit->setMinimumSize(400, 100);
|
||||
_textEdit->setReadOnly(true);
|
||||
mainLayout->addWidget(_textEdit);
|
||||
|
||||
addText(QStringLiteral("Note: pressing Cancel will almost certainly cause the terminal to be unusable."));
|
||||
addText(QStringLiteral("-----------------"));
|
||||
}
|
||||
|
||||
void ZModemDialog::addText(const QString &text)
|
||||
{
|
||||
_textEdit->append(text);
|
||||
}
|
||||
|
||||
void ZModemDialog::addProgressText(const QString &text)
|
||||
{
|
||||
_textEdit->insertPlainText(text);
|
||||
}
|
||||
|
||||
void ZModemDialog::slotCancel()
|
||||
{
|
||||
Q_EMIT zmodemCancel();
|
||||
slotClose();
|
||||
}
|
||||
|
||||
void ZModemDialog::transferDone()
|
||||
{
|
||||
mButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
|
||||
mButtonBox->button(QDialogButtonBox::Close)->setEnabled(true);
|
||||
}
|
||||
|
||||
void ZModemDialog::slotClose()
|
||||
{
|
||||
delayedDestruct();
|
||||
accept();
|
||||
}
|
||||
|
||||
void ZModemDialog::delayedDestruct()
|
||||
{
|
||||
if (isVisible()) {
|
||||
hide();
|
||||
}
|
||||
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
#include "moc_ZModemDialog.cpp"
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2002 Waldo Bastian <bastian@kde.org>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0-only
|
||||
**/
|
||||
|
||||
#ifndef ZMODEM_DIALOG_H
|
||||
#define ZMODEM_DIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "konsoleprivate_export.h"
|
||||
|
||||
class KTextEdit;
|
||||
class QDialogButtonBox;
|
||||
namespace Konsole
|
||||
{
|
||||
class KONSOLEPRIVATE_EXPORT ZModemDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ZModemDialog(QWidget *parent, bool modal, const QString &caption);
|
||||
|
||||
/**
|
||||
* Adds a line of text to the progress window
|
||||
*/
|
||||
void addText(const QString &);
|
||||
|
||||
/**
|
||||
* Adds a line of text without a new line to the progress window
|
||||
*/
|
||||
void addProgressText(const QString &);
|
||||
|
||||
/**
|
||||
* To indicate the process is finished.
|
||||
*/
|
||||
void transferDone();
|
||||
|
||||
Q_SIGNALS:
|
||||
void zmodemCancel();
|
||||
|
||||
private Q_SLOTS:
|
||||
void slotClose();
|
||||
void slotCancel();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(ZModemDialog)
|
||||
|
||||
void delayedDestruct();
|
||||
KTextEdit *_textEdit;
|
||||
QDialogButtonBox *mButtonBox;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2019 Kurt Hindenburg <kurt.hindenburg@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "BookMarkTest.h"
|
||||
|
||||
// Qt
|
||||
#include <QTest>
|
||||
|
||||
// KDE
|
||||
#include <KBookmarkManager>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
/* This does not use Konsole's BookmarkHandler directly; it is used to
|
||||
* test the code copied from there and to test any changes.
|
||||
*/
|
||||
|
||||
/* Test that the URL (command) does not get mangled by KBookMark's encoding */
|
||||
void BookMarkTest::testBookMarkURLs_data()
|
||||
{
|
||||
auto testData = QFINDTESTDATA(QStringLiteral("data/bookmarks.xml"));
|
||||
auto bookmarkManager = std::make_unique<KBookmarkManager>(testData);
|
||||
auto groupUrlList = bookmarkManager->root().groupUrlList();
|
||||
|
||||
// text explaining test, correct test result
|
||||
QStringList bm_urls = {QStringLiteral("simple command"),
|
||||
QStringLiteral("ssh machine"),
|
||||
QStringLiteral("command with pipe (|)"),
|
||||
QStringLiteral("ssh machine | tee -a /var/log/system.log"),
|
||||
QStringLiteral("file URL w/ non ASCII part"),
|
||||
QStringLiteral("file:///home/user/aκόσμε"),
|
||||
QStringLiteral("command with double quotes"),
|
||||
QStringLiteral("isql-fb -u sysdba -p example \"test\""),
|
||||
QStringLiteral("command with single quotes"),
|
||||
QStringLiteral("isql-fb -u sysdba -p example 'test'"),
|
||||
QStringLiteral("command with %"),
|
||||
QStringLiteral("date +%m-%d-%Y")};
|
||||
|
||||
QTest::addColumn<QString>("text");
|
||||
QTest::addColumn<QString>("result");
|
||||
for (int i = 0; i < groupUrlList.size(); ++i) {
|
||||
const auto &bm_url = groupUrlList.at(i);
|
||||
// Verify this line matching SessionControll.cpp to validate tests
|
||||
auto bm = QUrl::fromPercentEncoding(bm_url.toEncoded());
|
||||
QTest::newRow(bm_urls.at(i * 2).toUtf8().data()) << bm_urls.at((i * 2) + 1) << bm;
|
||||
}
|
||||
}
|
||||
|
||||
// Only test top level URLs (no folders)
|
||||
void BookMarkTest::testBookMarkURLs()
|
||||
{
|
||||
QFETCH(QString, text);
|
||||
QFETCH(QString, result);
|
||||
|
||||
QCOMPARE(text, result);
|
||||
}
|
||||
|
||||
/*
|
||||
* When testing more than just URLs, the below loop can be used
|
||||
auto bm = root.first();
|
||||
int i = 0;
|
||||
while (!bm.isNull()) {
|
||||
auto bm_url = QUrl::fromPercentEncoding(bm.url().toString().toUtf8());
|
||||
qWarning()<< i << ":" <<bm.url().toString();
|
||||
i++;
|
||||
bm = root.next(bm);
|
||||
}
|
||||
*/
|
||||
|
||||
QTEST_GUILESS_MAIN(BookMarkTest)
|
||||
|
||||
#include "moc_BookMarkTest.cpp"
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2019 Kurt Hindenburg <kurt.hindenburg@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BOOKMARKTEST_H
|
||||
#define BOOKMARKTEST_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class BookMarkTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void testBookMarkURLs_data();
|
||||
void testBookMarkURLs();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // BOOKMARKTEST_H
|
||||
@@ -0,0 +1,47 @@
|
||||
include(ECMAddTests)
|
||||
|
||||
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(KONSOLE_TEST_LIBS Qt::Test konsoleprivate konsolecharacters konsoledecoders)
|
||||
|
||||
# Temporary bypass for BKO 432379
|
||||
if(BUILD_SHARED_LIBS)
|
||||
ecm_add_test(KeyboardTranslatorTest.cpp LINK_LIBRARIES ${KONSOLE_TEST_LIBS})
|
||||
endif()
|
||||
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT WIN32)
|
||||
ecm_add_test(
|
||||
PartTest.cpp
|
||||
LINK_LIBRARIES KF6::XmlGui KF6::Parts KF6::Pty ${KONSOLE_TEST_LIBS}
|
||||
)
|
||||
endif()
|
||||
|
||||
ecm_add_tests(
|
||||
BookMarkTest.cpp
|
||||
CharacterColorTest.cpp
|
||||
CharacterTest.cpp
|
||||
CharacterWidthTest.cpp
|
||||
HotSpotFilterTest.cpp
|
||||
ProcessInfoTest.cpp
|
||||
ProfileTest.cpp
|
||||
ScreenTest.cpp
|
||||
ShellCommandTest.cpp
|
||||
TerminalCharacterDecoderTest.cpp
|
||||
Vt102EmulationTest.cpp
|
||||
LINK_LIBRARIES ${KONSOLE_TEST_LIBS}
|
||||
)
|
||||
|
||||
if(NOT WIN32)
|
||||
ecm_add_tests(
|
||||
PtyTest.cpp
|
||||
LINK_LIBRARIES KF6::Pty ${KONSOLE_TEST_LIBS}
|
||||
)
|
||||
endif()
|
||||
|
||||
ecm_add_tests(
|
||||
HistoryTest.cpp
|
||||
SessionTest.cpp
|
||||
TerminalInterfaceTest.cpp
|
||||
TerminalTest.cpp
|
||||
LINK_LIBRARIES ${KONSOLE_TEST_LIBS} KF6::Parts
|
||||
)
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "CharacterColorTest.h"
|
||||
|
||||
// Qt
|
||||
|
||||
// KDE
|
||||
#include <QTest>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
const QColor CharacterColorTest::DefaultColorTable[TABLE_COLORS] = {
|
||||
QColor(0x00, 0x00, 0x00), // Dfore
|
||||
QColor(0xFF, 0xFF, 0xFF), // Dback
|
||||
QColor(0x00, 0x00, 0x00), // Black
|
||||
QColor(0xB2, 0x18, 0x18), // Red
|
||||
QColor(0x18, 0xB2, 0x18), // Green
|
||||
QColor(0xB2, 0x68, 0x18), // Yellow
|
||||
QColor(0x18, 0x18, 0xB2), // Blue
|
||||
QColor(0xB2, 0x18, 0xB2), // Magenta
|
||||
QColor(0x18, 0xB2, 0xB2), // Cyan
|
||||
QColor(0xB2, 0xB2, 0xB2), // White
|
||||
// intensive versions
|
||||
QColor(0x00, 0x00, 0x00),
|
||||
QColor(0xFF, 0xFF, 0xFF),
|
||||
QColor(0x68, 0x68, 0x68),
|
||||
QColor(0xFF, 0x54, 0x54),
|
||||
QColor(0x54, 0xFF, 0x54),
|
||||
QColor(0xFF, 0xFF, 0x54),
|
||||
QColor(0x54, 0x54, 0xFF),
|
||||
QColor(0xFF, 0x54, 0xFF),
|
||||
QColor(0x54, 0xFF, 0xFF),
|
||||
QColor(0xFF, 0xFF, 0xFF),
|
||||
// Here are faint intensities, which may not be good.
|
||||
// faint versions
|
||||
QColor(0x00, 0x00, 0x00),
|
||||
QColor(0xFF, 0xFF, 0xFF),
|
||||
QColor(0x00, 0x00, 0x00),
|
||||
QColor(0x65, 0x00, 0x00),
|
||||
QColor(0x00, 0x65, 0x00),
|
||||
QColor(0x65, 0x5E, 0x00),
|
||||
QColor(0x00, 0x00, 0x65),
|
||||
QColor(0x65, 0x00, 0x65),
|
||||
QColor(0x00, 0x65, 0x65),
|
||||
QColor(0x65, 0x65, 0x65),
|
||||
};
|
||||
|
||||
void CharacterColorTest::init()
|
||||
{
|
||||
}
|
||||
|
||||
void CharacterColorTest::cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
void CharacterColorTest::testColorEntry()
|
||||
{
|
||||
QColor black = QColor(0x00, 0x00, 0x00);
|
||||
QColor white = QColor(0xFF, 0xFF, 0xFF);
|
||||
QColor red = QColor(0xB2, 0x18, 0x18);
|
||||
QColor green = QColor(0x18, 0xB2, 0x18);
|
||||
|
||||
// Test operator== operator!=
|
||||
QCOMPARE(black == white, false);
|
||||
QCOMPARE(black != white, true);
|
||||
QCOMPARE(red == green, false);
|
||||
QCOMPARE(red != green, true);
|
||||
|
||||
QCOMPARE(red == green, false);
|
||||
QCOMPARE(red != green, true);
|
||||
|
||||
// Test operator=
|
||||
QColor tmpColorEntry;
|
||||
tmpColorEntry = red;
|
||||
QCOMPARE(tmpColorEntry == red, true);
|
||||
QCOMPARE(red == tmpColorEntry, true);
|
||||
|
||||
// Test QColor()
|
||||
QColor defaultColorEntry = QColor();
|
||||
QCOMPARE(defaultColorEntry != green, true);
|
||||
QCOMPARE(defaultColorEntry != black, true);
|
||||
QCOMPARE(defaultColorEntry.isValid(), false);
|
||||
}
|
||||
|
||||
void CharacterColorTest::testDummyConstructor()
|
||||
{
|
||||
CharacterColor charColor;
|
||||
QCOMPARE(charColor.isValid(), false);
|
||||
}
|
||||
|
||||
void CharacterColorTest::testColorSpaceDefault_data()
|
||||
{
|
||||
QTest::addColumn<int>("colorValue");
|
||||
QTest::addColumn<QColor>("expected");
|
||||
|
||||
QTest::newRow("color 0") << 0 << DefaultColorTable[0];
|
||||
QTest::newRow("color 1") << 1 << DefaultColorTable[1];
|
||||
}
|
||||
|
||||
void CharacterColorTest::testColorSpaceDefault()
|
||||
{
|
||||
QFETCH(int, colorValue);
|
||||
QFETCH(QColor, expected);
|
||||
|
||||
CharacterColor charColor(COLOR_SPACE_DEFAULT, colorValue);
|
||||
const QColor result = charColor.color(DefaultColorTable);
|
||||
|
||||
QCOMPARE(result, expected);
|
||||
}
|
||||
|
||||
void CharacterColorTest::testColorSpaceSystem_data()
|
||||
{
|
||||
QTest::addColumn<int>("colorValue");
|
||||
QTest::addColumn<QColor>("expected");
|
||||
|
||||
QTest::newRow("color 0") << 0 << DefaultColorTable[2 + 0];
|
||||
QTest::newRow("color 1") << 1 << DefaultColorTable[2 + 1];
|
||||
QTest::newRow("color 7") << 7 << DefaultColorTable[2 + 7];
|
||||
}
|
||||
|
||||
void CharacterColorTest::testColorSpaceSystem()
|
||||
{
|
||||
QFETCH(int, colorValue);
|
||||
QFETCH(QColor, expected);
|
||||
|
||||
CharacterColor charColor(COLOR_SPACE_SYSTEM, colorValue);
|
||||
const QColor result = charColor.color(DefaultColorTable);
|
||||
|
||||
QCOMPARE(result, expected);
|
||||
}
|
||||
|
||||
void CharacterColorTest::testColorSpaceRGB_data()
|
||||
{
|
||||
QTest::addColumn<int>("colorValue");
|
||||
QTest::addColumn<QColor>("expected");
|
||||
|
||||
// Pick colors to test or test all if needed
|
||||
for (const int i : {0, 1, 64, 127, 128, 215, 255}) {
|
||||
const QString name = QStringLiteral("color %1").arg(i);
|
||||
QTest::newRow(qPrintable(name)) << i << QColor(i >> 16, i >> 8, i);
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterColorTest::testColorSpaceRGB()
|
||||
{
|
||||
QFETCH(int, colorValue);
|
||||
QFETCH(QColor, expected);
|
||||
|
||||
CharacterColor charColor(COLOR_SPACE_RGB, colorValue);
|
||||
const QColor result = charColor.color(DefaultColorTable);
|
||||
|
||||
QCOMPARE(result, expected);
|
||||
}
|
||||
void CharacterColorTest::testColor256_data()
|
||||
{
|
||||
QTest::addColumn<int>("colorValue");
|
||||
QTest::addColumn<QColor>("expected");
|
||||
|
||||
// This might be overkill
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
const QString name = QStringLiteral("color256 color %1").arg(i);
|
||||
QTest::newRow(qPrintable(name)) << i << DefaultColorTable[i + 2];
|
||||
}
|
||||
for (int i = 8; i < 16; ++i) {
|
||||
const QString name = QStringLiteral("color256 color %1").arg(i);
|
||||
QTest::newRow(qPrintable(name)) << i << DefaultColorTable[i + 2 + 10 - 8];
|
||||
}
|
||||
for (int i = 16; i < 232; ++i) {
|
||||
const QString name = QStringLiteral("color256 color %1").arg(i);
|
||||
const auto u = i - 16;
|
||||
const auto color = QColor(((u / 36) % 6) != 0 ? (40 * ((u / 36) % 6) + 55) : 0,
|
||||
((u / 6) % 6) != 0 ? (40 * ((u / 6) % 6) + 55) : 0,
|
||||
((u / 1) % 6) != 0 ? (40 * ((u / 1) % 6) + 55) : 0);
|
||||
QTest::newRow(qPrintable(name)) << i << color;
|
||||
}
|
||||
for (int i = 232; i < 256; ++i) {
|
||||
const QString name = QStringLiteral("color256 color %1").arg(i);
|
||||
const auto gray = (i - 232) * 10 + 8;
|
||||
QTest::newRow(qPrintable(name)) << i << QColor(gray, gray, gray);
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterColorTest::testColor256()
|
||||
{
|
||||
QFETCH(int, colorValue);
|
||||
QFETCH(QColor, expected);
|
||||
|
||||
const QColor result = color256(colorValue, DefaultColorTable);
|
||||
QCOMPARE(result, expected);
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(CharacterColorTest)
|
||||
|
||||
#include "moc_CharacterColorTest.cpp"
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef CHARACTERCOLORTEST_H
|
||||
#define CHARACTERCOLORTEST_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "../characters/CharacterColor.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class CharacterColorTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
void testColorEntry();
|
||||
|
||||
void testDummyConstructor();
|
||||
void testColorSpaceDefault_data();
|
||||
void testColorSpaceDefault();
|
||||
void testColorSpaceSystem_data();
|
||||
void testColorSpaceSystem();
|
||||
void testColorSpaceRGB_data();
|
||||
void testColorSpaceRGB();
|
||||
void testColor256_data();
|
||||
void testColor256();
|
||||
|
||||
private:
|
||||
static const QColor DefaultColorTable[];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CHARACTERCOLORTEST_H
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2019 Tomaz Canabrava <tomaz.canabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "CharacterTest.h"
|
||||
#include "Character.h"
|
||||
|
||||
#include <QTest>
|
||||
#include <cstdint>
|
||||
|
||||
QTEST_GUILESS_MAIN(Konsole::CharacterTest)
|
||||
|
||||
#include "moc_CharacterTest.cpp"
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2019 Tomaz Canabrava <tomaz.canabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class CharacterTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Kurt Hindenburg <kurt.hindenburg@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "CharacterWidthTest.h"
|
||||
|
||||
// KDE
|
||||
#include <QTest>
|
||||
|
||||
#include "../characters/Character.h"
|
||||
#include "konsoleprivate_export.h"
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
void CharacterWidthTest::testWidth_data()
|
||||
{
|
||||
QTest::addColumn<uint>("character");
|
||||
QTest::addColumn<int>("width");
|
||||
|
||||
/* This is a work in progress.... */
|
||||
|
||||
/* konsole_wcwidth uses -1 C0/C1 and DEL */
|
||||
QTest::newRow("0x007F") << uint(0x007F) << -1;
|
||||
|
||||
QTest::newRow("0x0300") << uint(0x0300) << 0;
|
||||
QTest::newRow("0x070F") << uint(0x070F) << 0;
|
||||
QTest::newRow("0x1160") << uint(0x1160) << 0;
|
||||
QTest::newRow("0x10300") << uint(0x10300) << 1;
|
||||
|
||||
QTest::newRow("a") << uint('a') << 1;
|
||||
QTest::newRow("0x00AD") << uint(0x00AD) << 1;
|
||||
QTest::newRow("0x00A0") << uint(0x00A0) << 1;
|
||||
QTest::newRow("0x10FB") << uint(0x10FB) << 1;
|
||||
QTest::newRow("0xFF76") << uint(0xFF76) << 1;
|
||||
QTest::newRow("0x103A0") << uint(0x103A0) << 1;
|
||||
QTest::newRow("0x10A06") << uint(0x10A06) << 0;
|
||||
|
||||
QTest::newRow("0x3000") << uint(0x3000) << 2;
|
||||
QTest::newRow("0x300a") << uint(0x300a) << 2;
|
||||
QTest::newRow("0x300b") << uint(0x300b) << 2;
|
||||
QTest::newRow("0xFF01") << uint(0xFF01) << 2;
|
||||
QTest::newRow("0xFF5F") << uint(0xFF5F) << 2;
|
||||
QTest::newRow("0xFF60") << uint(0xFF60) << 2;
|
||||
QTest::newRow("0xFFe0") << uint(0xFFe6) << 2;
|
||||
|
||||
QTest::newRow("0x1F943 tumbler glass") << uint(0x1F943) << 2;
|
||||
QTest::newRow("0x1F944 spoon") << uint(0x1F944) << 2;
|
||||
|
||||
QTest::newRow("0x26A1 high voltage sign (BUG 378124)") << uint(0x026A1) << 2;
|
||||
QTest::newRow("0x2615 hot beverage (BUG 392171)") << uint(0x02615) << 2;
|
||||
QTest::newRow("0x26EA church (BUG 392171)") << uint(0x026EA) << 2;
|
||||
QTest::newRow("0x1D11E musical symbol g clef (BUG 339439)") << uint(0x1D11E) << 1;
|
||||
}
|
||||
|
||||
void CharacterWidthTest::testWidth()
|
||||
{
|
||||
QFETCH(uint, character);
|
||||
|
||||
QTEST(Character::width(character), "width");
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(CharacterWidthTest)
|
||||
|
||||
#include "moc_CharacterWidthTest.cpp"
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Kurt Hindenburg <kurt.hindenburg@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef CHARACTERWIDTHTEST_H
|
||||
#define CHARACTERWIDTHTEST_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class CharacterWidthTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
|
||||
void testWidth_data();
|
||||
void testWidth();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CHARACTERWIDTHTEST_H
|
||||
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2010 Kurt Hindenburg <kurt.hindenburg@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "DBusTest.h"
|
||||
#include "../profile/Profile.h"
|
||||
#include "../profile/ProfileWriter.h"
|
||||
#include "../session/Session.h"
|
||||
|
||||
// Qt
|
||||
#include <QProcess>
|
||||
#include <QRandomGenerator>
|
||||
#include <QSignalSpy>
|
||||
#include <QStandardPaths>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
/* Exec a new Konsole and grab its dbus */
|
||||
void DBusTest::initTestCase()
|
||||
{
|
||||
qRegisterMetaType<QProcess::ExitStatus>();
|
||||
|
||||
const QString interfaceName = QStringLiteral("org.kde.konsole");
|
||||
QDBusConnectionInterface *bus = nullptr;
|
||||
QStringList konsoleServices;
|
||||
|
||||
if (!QDBusConnection::sessionBus().isConnected() || ((bus = QDBusConnection::sessionBus().interface()) == nullptr)) {
|
||||
QFAIL("Session bus not found");
|
||||
}
|
||||
|
||||
QDBusReply<QStringList> serviceReply = bus->registeredServiceNames();
|
||||
QVERIFY2(serviceReply.isValid(), "SessionBus interfaces not available");
|
||||
|
||||
// Find all current Konsoles' services running
|
||||
QStringList allServices = serviceReply;
|
||||
for (QStringList::const_iterator it = allServices.constBegin(), end = allServices.constEnd(); it != end; ++it) {
|
||||
const QString service = *it;
|
||||
if (service.startsWith(interfaceName)) {
|
||||
konsoleServices << service;
|
||||
}
|
||||
}
|
||||
|
||||
// Create test profile
|
||||
auto profile = Profile::Ptr(new Profile());
|
||||
ProfileWriter profileWriter;
|
||||
|
||||
do {
|
||||
_testProfileName = QStringLiteral("konsole-dbus-test-profile-%1").arg(QRandomGenerator::global()->generate());
|
||||
profile->setProperty(Profile::UntranslatedName, _testProfileName);
|
||||
profile->setProperty(Profile::Name, _testProfileName);
|
||||
_testProfilePath = profileWriter.getPath(profile);
|
||||
} while (QFile::exists(_testProfilePath));
|
||||
_testProfileEnv = QStringLiteral("TEST_PROFILE=%1").arg(_testProfileName);
|
||||
profile->setProperty(Profile::Environment, QStringList(_testProfileEnv));
|
||||
// %D = Current Directory (Long) - hacky way to check current directory
|
||||
profile->setProperty(Profile::LocalTabTitleFormat, QStringLiteral("%D"));
|
||||
profileWriter.writeProfile(_testProfilePath, profile);
|
||||
|
||||
// Create a new Konsole with a separate process id
|
||||
_process = new QProcess;
|
||||
_process->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
_process->start(QStringLiteral("konsole"), QStringList(QStringLiteral("--separate")));
|
||||
|
||||
if (!_process->waitForStarted()) {
|
||||
QFAIL(QStringLiteral("Unable to exec a new Konsole").toLatin1().data());
|
||||
}
|
||||
|
||||
// Wait for above Konsole to finish starting
|
||||
QThread::sleep(5);
|
||||
|
||||
serviceReply = bus->registeredServiceNames();
|
||||
QVERIFY2(serviceReply.isValid(), "SessionBus interfaces not available");
|
||||
|
||||
// Find dbus service of above Konsole
|
||||
allServices = serviceReply;
|
||||
for (QStringList::const_iterator it = allServices.constBegin(), end = allServices.constEnd(); it != end; ++it) {
|
||||
const QString service = *it;
|
||||
if (service.startsWith(interfaceName)) {
|
||||
if (!konsoleServices.contains(service)) {
|
||||
_interfaceName = service;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVERIFY2(!_interfaceName.isEmpty(), "This test will only work in a Konsole window with a new PID. A new Konsole PID can't be found.");
|
||||
|
||||
QDBusInterface iface(_interfaceName, QStringLiteral("/Windows/1"), QStringLiteral("org.kde.konsole.Konsole"));
|
||||
QVERIFY(iface.isValid());
|
||||
}
|
||||
|
||||
/* Close the Konsole window that was opened to test the DBus interface
|
||||
*/
|
||||
void DBusTest::cleanupTestCase()
|
||||
{
|
||||
// Remove test profile
|
||||
QFile::remove(_testProfilePath);
|
||||
|
||||
// Need to take care of when user has CloseAllTabs=False otherwise
|
||||
// they will get a popup dialog when we try to close this.
|
||||
QSignalSpy quitSpy(_process, SIGNAL(finished(int, QProcess::ExitStatus)));
|
||||
|
||||
// Do not use QWidget::close(), as it shows question popup when
|
||||
// CloseAllTabs is set to false (default).
|
||||
QDBusInterface iface(_interfaceName, QStringLiteral("/MainApplication"), QStringLiteral("org.qtproject.Qt.QCoreApplication"));
|
||||
QVERIFY2(iface.isValid(), "Unable to get a dbus interface to Konsole!");
|
||||
|
||||
QDBusReply<void> instanceReply = iface.call(QStringLiteral("quit"));
|
||||
if (!instanceReply.isValid()) {
|
||||
QFAIL(QStringLiteral("Unable to close Konsole: %1").arg(instanceReply.error().message()).toLatin1().data());
|
||||
}
|
||||
quitSpy.wait();
|
||||
_process->kill();
|
||||
_process->deleteLater();
|
||||
}
|
||||
|
||||
void DBusTest::testSessions()
|
||||
{
|
||||
QDBusReply<void> voidReply;
|
||||
QDBusReply<bool> boolReply;
|
||||
QDBusReply<QByteArray> arrayReply;
|
||||
QDBusReply<QString> stringReply;
|
||||
QDBusReply<QStringList> listReply;
|
||||
|
||||
QDBusInterface iface(_interfaceName, QStringLiteral("/Sessions/1"), QStringLiteral("org.kde.konsole.Session"));
|
||||
QVERIFY(iface.isValid());
|
||||
|
||||
//****************** Test is/set MonitorActivity
|
||||
voidReply = iface.call(QStringLiteral("setMonitorActivity"), false);
|
||||
QVERIFY(voidReply.isValid());
|
||||
|
||||
boolReply = iface.call(QStringLiteral("isMonitorActivity"));
|
||||
QVERIFY(boolReply.isValid());
|
||||
QCOMPARE(boolReply.value(), false);
|
||||
|
||||
voidReply = iface.call(QStringLiteral("setMonitorActivity"), true);
|
||||
QVERIFY(voidReply.isValid());
|
||||
|
||||
boolReply = iface.call(QStringLiteral("isMonitorActivity"));
|
||||
QVERIFY(boolReply.isValid());
|
||||
QCOMPARE(boolReply.value(), true);
|
||||
|
||||
//****************** Test is/set MonitorSilence
|
||||
voidReply = iface.call(QStringLiteral("setMonitorSilence"), false);
|
||||
QVERIFY(voidReply.isValid());
|
||||
|
||||
boolReply = iface.call(QStringLiteral("isMonitorSilence"));
|
||||
QVERIFY(boolReply.isValid());
|
||||
QCOMPARE(boolReply.value(), false);
|
||||
|
||||
voidReply = iface.call(QStringLiteral("setMonitorSilence"), true);
|
||||
QVERIFY(voidReply.isValid());
|
||||
|
||||
boolReply = iface.call(QStringLiteral("isMonitorSilence"));
|
||||
QVERIFY(boolReply.isValid());
|
||||
QCOMPARE(boolReply.value(), true);
|
||||
|
||||
//****************** Test codec and setCodec
|
||||
arrayReply = iface.call(QStringLiteral("codec"));
|
||||
QVERIFY(arrayReply.isValid());
|
||||
|
||||
// Obtain a list of system's Codecs
|
||||
QList<QByteArray> availableCodecs = QTextCodec::availableCodecs();
|
||||
for (int i = 0; i < availableCodecs.count(); ++i) {
|
||||
boolReply = iface.call(QStringLiteral("setCodec"), availableCodecs[i]);
|
||||
QVERIFY(boolReply.isValid());
|
||||
QCOMPARE(boolReply.value(), true);
|
||||
|
||||
arrayReply = iface.call(QStringLiteral("codec"));
|
||||
QVERIFY(arrayReply.isValid());
|
||||
// Compare result with name due to aliases issue
|
||||
// Better way to do this?
|
||||
QCOMPARE((QTextCodec::codecForName(arrayReply.value()))->name(), (QTextCodec::codecForName(availableCodecs[i]))->name());
|
||||
}
|
||||
|
||||
//****************** Test is/set flowControlEnabled
|
||||
voidReply = iface.call(QStringLiteral("setFlowControlEnabled"), true);
|
||||
QVERIFY(voidReply.isValid());
|
||||
|
||||
boolReply = iface.call(QStringLiteral("flowControlEnabled"));
|
||||
QVERIFY(boolReply.isValid());
|
||||
QCOMPARE(boolReply.value(), true);
|
||||
|
||||
voidReply = iface.call(QStringLiteral("setFlowControlEnabled"), false);
|
||||
QVERIFY(voidReply.isValid());
|
||||
|
||||
boolReply = iface.call(QStringLiteral("flowControlEnabled"));
|
||||
QVERIFY(boolReply.isValid());
|
||||
QCOMPARE(boolReply.value(), false);
|
||||
|
||||
//****************** Test is/set environment
|
||||
listReply = iface.call(QStringLiteral("environment"));
|
||||
QVERIFY(listReply.isValid());
|
||||
|
||||
QStringList prevEnv = listReply.value();
|
||||
// for (int i = 0; i < prevEnv.size(); ++i)
|
||||
// qDebug()<< prevEnv.at(i).toLocal8Bit().constData() << endl;
|
||||
|
||||
voidReply = iface.call(QStringLiteral("setEnvironment"), QStringList());
|
||||
QVERIFY(voidReply.isValid());
|
||||
|
||||
listReply = iface.call(QStringLiteral("environment"));
|
||||
QVERIFY(listReply.isValid());
|
||||
QCOMPARE(listReply.value(), QStringList());
|
||||
|
||||
voidReply = iface.call(QStringLiteral("setEnvironment"), prevEnv);
|
||||
QVERIFY(voidReply.isValid());
|
||||
|
||||
listReply = iface.call(QStringLiteral("environment"));
|
||||
QVERIFY(listReply.isValid());
|
||||
QCOMPARE(listReply.value(), prevEnv);
|
||||
|
||||
//****************** Test is/set title
|
||||
// TODO: Consider checking what is in Profile
|
||||
stringReply = iface.call(QStringLiteral("title"), Session::NameRole);
|
||||
QVERIFY(stringReply.isValid());
|
||||
|
||||
// set title to, what title should be
|
||||
QMap<QString, QString> titleMap;
|
||||
titleMap[QStringLiteral("Shell")] = QStringLiteral("Shell");
|
||||
|
||||
// BUG: It appears that Session::LocalTabTitle is set to Shell and
|
||||
// doesn't change. While RemoteTabTitle is actually the LocalTabTitle
|
||||
// TODO: Figure out what's going on...
|
||||
QMapIterator<QString, QString> i(titleMap);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
voidReply = iface.call(QStringLiteral("setTitle"), Session::NameRole, i.key());
|
||||
QVERIFY(voidReply.isValid());
|
||||
|
||||
stringReply = iface.call(QStringLiteral("title"), Session::NameRole);
|
||||
QVERIFY(stringReply.isValid());
|
||||
|
||||
QCOMPARE(stringReply.value(), i.value());
|
||||
}
|
||||
}
|
||||
|
||||
void DBusTest::testWindows()
|
||||
{
|
||||
// Tested functions:
|
||||
// [+] int sessionCount();
|
||||
// [+] QStringList sessionList();
|
||||
// [+] int currentSession();
|
||||
// [+] void setCurrentSession(int sessionId);
|
||||
// [+] int newSession();
|
||||
// [+] int newSession(const QString &profile);
|
||||
// [+] int newSession(const QString &profile, const QString &directory);
|
||||
// [ ] QString defaultProfile();
|
||||
// [ ] QStringList profileList();
|
||||
// [ ] void nextSession();
|
||||
// [ ] void prevSession();
|
||||
// [ ] void moveSessionLeft();
|
||||
// [ ] void moveSessionRight();
|
||||
// [ ] void setTabWidthToText(bool);
|
||||
|
||||
QDBusReply<int> intReply;
|
||||
QDBusReply<QStringList> listReply;
|
||||
QDBusReply<void> voidReply;
|
||||
QDBusReply<QString> stringReply;
|
||||
|
||||
int sessionCount = -1;
|
||||
int initialSessionId = -1;
|
||||
|
||||
QDBusInterface iface(_interfaceName, QStringLiteral("/Windows/1"), QStringLiteral("org.kde.konsole.Window"));
|
||||
QVERIFY(iface.isValid());
|
||||
|
||||
intReply = iface.call(QStringLiteral("sessionCount"));
|
||||
QVERIFY(intReply.isValid());
|
||||
sessionCount = intReply.value();
|
||||
QVERIFY(sessionCount > 0);
|
||||
|
||||
intReply = iface.call(QStringLiteral("currentSession"));
|
||||
QVERIFY(intReply.isValid());
|
||||
initialSessionId = intReply.value();
|
||||
|
||||
intReply = iface.call(QStringLiteral("newSession"));
|
||||
QVERIFY(intReply.isValid());
|
||||
++sessionCount;
|
||||
int newSessionId = intReply.value();
|
||||
QVERIFY(newSessionId != initialSessionId);
|
||||
|
||||
listReply = iface.call(QStringLiteral("sessionList"));
|
||||
QVERIFY(listReply.isValid());
|
||||
|
||||
QStringList sessions = listReply.value();
|
||||
QVERIFY(sessions.contains(QString::number(initialSessionId)));
|
||||
QVERIFY(sessions.contains(QString::number(newSessionId)));
|
||||
QVERIFY(sessions.size() == sessionCount);
|
||||
|
||||
intReply = iface.call(QStringLiteral("newSession"), _testProfileName);
|
||||
QVERIFY(intReply.isValid());
|
||||
++sessionCount;
|
||||
newSessionId = intReply.value();
|
||||
QVERIFY(newSessionId != initialSessionId);
|
||||
{
|
||||
QDBusInterface sessionIface(_interfaceName, QStringLiteral("/Sessions/%1").arg(newSessionId), QStringLiteral("org.kde.konsole.Session"));
|
||||
QVERIFY(iface.isValid());
|
||||
|
||||
listReply = sessionIface.call(QStringLiteral("environment"));
|
||||
QVERIFY(listReply.isValid());
|
||||
QStringList env = listReply.value();
|
||||
QVERIFY(env.contains(_testProfileEnv));
|
||||
}
|
||||
|
||||
const auto tempDirectories = QStandardPaths::standardLocations(QStandardPaths::TempLocation);
|
||||
const QString sessionDirectory = tempDirectories.empty() ? QStringLiteral("/") : tempDirectories.constFirst();
|
||||
intReply = iface.call(QStringLiteral("newSession"), _testProfileName, sessionDirectory);
|
||||
QVERIFY(intReply.isValid());
|
||||
++sessionCount;
|
||||
newSessionId = intReply.value();
|
||||
QVERIFY(newSessionId != initialSessionId);
|
||||
{
|
||||
QDBusInterface sessionIface(_interfaceName, QStringLiteral("/Sessions/%1").arg(newSessionId), QStringLiteral("org.kde.konsole.Session"));
|
||||
QVERIFY(iface.isValid());
|
||||
|
||||
listReply = sessionIface.call(QStringLiteral("environment"));
|
||||
QVERIFY(listReply.isValid());
|
||||
QStringList env = listReply.value();
|
||||
QVERIFY(env.contains(_testProfileEnv));
|
||||
|
||||
// Apparently there's no function for checking CWD.
|
||||
// The profile uses "%D" as title format, so check the title
|
||||
stringReply = sessionIface.call(QStringLiteral("title"), Session::DisplayedTitleRole);
|
||||
QVERIFY(stringReply.isValid());
|
||||
QVERIFY(QDir(stringReply.value()) == QDir(sessionDirectory));
|
||||
}
|
||||
|
||||
voidReply = iface.call(QStringLiteral("setCurrentSession"), initialSessionId);
|
||||
QVERIFY(voidReply.isValid());
|
||||
|
||||
intReply = iface.call(QStringLiteral("currentSession"));
|
||||
QVERIFY(intReply.isValid());
|
||||
QVERIFY(intReply.value() == initialSessionId);
|
||||
|
||||
intReply = iface.call(QStringLiteral("sessionCount"));
|
||||
QVERIFY(intReply.isValid());
|
||||
QVERIFY(intReply.value() == sessionCount);
|
||||
}
|
||||
|
||||
QTEST_MAIN(DBusTest)
|
||||
|
||||
#include "moc_DBusTest.cpp"
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2010 Kurt Hindenburg <kurt.hindenburg@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef DBUSTEST_H
|
||||
#define DBUSTEST_H
|
||||
|
||||
#include <QDBusConnectionInterface>
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusReply>
|
||||
#include <QTest>
|
||||
#include <QTextCodec>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
class QProcess;
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class DBusTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void testSessions();
|
||||
void testWindows();
|
||||
|
||||
// protected slots are not treated as test cases
|
||||
protected Q_SLOTS:
|
||||
|
||||
private:
|
||||
QString _interfaceName;
|
||||
QProcess *_process = nullptr;
|
||||
|
||||
QString _testProfileName;
|
||||
QString _testProfilePath;
|
||||
QString _testProfileEnv;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // DBUSTEST_H
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Kurt Hindenburg <kurt.hindenburg@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "HistoryTest.h"
|
||||
|
||||
#include <QTest>
|
||||
|
||||
// Konsole
|
||||
#include "../Emulation.h"
|
||||
#include "../session/Session.h"
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
void HistoryTest::initTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void HistoryTest::cleanupTestCase()
|
||||
{
|
||||
delete[] testImage;
|
||||
}
|
||||
|
||||
void HistoryTest::testHistoryNone()
|
||||
{
|
||||
std::unique_ptr<HistoryType> historyTypeNone = std::make_unique<HistoryTypeNone>();
|
||||
QCOMPARE(historyTypeNone->isEnabled(), false);
|
||||
QCOMPARE(historyTypeNone->isUnlimited(), false);
|
||||
QCOMPARE(historyTypeNone->maximumLineCount(), 0);
|
||||
}
|
||||
|
||||
void HistoryTest::testHistoryFile()
|
||||
{
|
||||
std::unique_ptr<HistoryType> historyTypeFile = std::make_unique<HistoryTypeFile>();
|
||||
QCOMPARE(historyTypeFile->isEnabled(), true);
|
||||
QCOMPARE(historyTypeFile->isUnlimited(), true);
|
||||
QCOMPARE(historyTypeFile->maximumLineCount(), -1);
|
||||
}
|
||||
|
||||
void HistoryTest::testCompactHistory()
|
||||
{
|
||||
std::unique_ptr<HistoryType> historyTypeCompact = std::make_unique<CompactHistoryType>(42);
|
||||
QCOMPARE(historyTypeCompact->isEnabled(), true);
|
||||
QCOMPARE(historyTypeCompact->isUnlimited(), false);
|
||||
QCOMPARE(historyTypeCompact->maximumLineCount(), 42);
|
||||
}
|
||||
|
||||
void HistoryTest::testEmulationHistory()
|
||||
{
|
||||
auto session = std::make_unique<Session>();
|
||||
Emulation *emulation = session->emulation();
|
||||
|
||||
const HistoryType &historyTypeDefault = emulation->history();
|
||||
QCOMPARE(historyTypeDefault.isEnabled(), false);
|
||||
QCOMPARE(historyTypeDefault.isUnlimited(), false);
|
||||
QCOMPARE(historyTypeDefault.maximumLineCount(), 0);
|
||||
|
||||
emulation->setHistory(HistoryTypeNone());
|
||||
const HistoryType &historyTypeNone = emulation->history();
|
||||
QCOMPARE(historyTypeNone.isEnabled(), false);
|
||||
QCOMPARE(historyTypeNone.isUnlimited(), false);
|
||||
QCOMPARE(historyTypeNone.maximumLineCount(), 0);
|
||||
|
||||
emulation->setHistory(HistoryTypeFile());
|
||||
const HistoryType &historyTypeFile = emulation->history();
|
||||
QCOMPARE(historyTypeFile.isEnabled(), true);
|
||||
QCOMPARE(historyTypeFile.isUnlimited(), true);
|
||||
QCOMPARE(historyTypeFile.maximumLineCount(), -1);
|
||||
|
||||
emulation->setHistory(CompactHistoryType(42));
|
||||
const HistoryType &compactHistoryType = emulation->history();
|
||||
QCOMPARE(compactHistoryType.isEnabled(), true);
|
||||
QCOMPARE(compactHistoryType.isUnlimited(), false);
|
||||
QCOMPARE(compactHistoryType.maximumLineCount(), 42);
|
||||
}
|
||||
|
||||
void HistoryTest::testHistoryScroll()
|
||||
{
|
||||
HistoryScroll *historyScroll;
|
||||
|
||||
// None
|
||||
historyScroll = new HistoryScrollNone();
|
||||
QVERIFY(!historyScroll->hasScroll());
|
||||
QCOMPARE(historyScroll->getLines(), 0);
|
||||
|
||||
const HistoryType &historyTypeNone = historyScroll->getType();
|
||||
QCOMPARE(historyTypeNone.isEnabled(), false);
|
||||
QCOMPARE(historyTypeNone.isUnlimited(), false);
|
||||
QCOMPARE(historyTypeNone.maximumLineCount(), 0);
|
||||
|
||||
delete historyScroll;
|
||||
|
||||
// File
|
||||
historyScroll = new HistoryScrollFile();
|
||||
QVERIFY(historyScroll->hasScroll());
|
||||
QCOMPARE(historyScroll->getLines(), 0);
|
||||
|
||||
const HistoryType &historyTypeFile = historyScroll->getType();
|
||||
QCOMPARE(historyTypeFile.isEnabled(), true);
|
||||
QCOMPARE(historyTypeFile.isUnlimited(), true);
|
||||
QCOMPARE(historyTypeFile.maximumLineCount(), -1);
|
||||
|
||||
delete historyScroll;
|
||||
|
||||
// Compact
|
||||
historyScroll = new CompactHistoryScroll(42);
|
||||
QVERIFY(historyScroll->hasScroll());
|
||||
QCOMPARE(historyScroll->getLines(), 0);
|
||||
|
||||
const HistoryType &compactHistoryType = historyScroll->getType();
|
||||
QCOMPARE(compactHistoryType.isEnabled(), true);
|
||||
QCOMPARE(compactHistoryType.isUnlimited(), false);
|
||||
QCOMPARE(compactHistoryType.maximumLineCount(), 42);
|
||||
|
||||
delete historyScroll;
|
||||
}
|
||||
|
||||
void HistoryTest::testHistoryReflow()
|
||||
{
|
||||
testImage = new Character[testStringSize];
|
||||
Character testChar;
|
||||
for (int i = 0; i < testStringSize; i++) {
|
||||
testImage[i] = Character((uint)testString[i]);
|
||||
}
|
||||
|
||||
// None
|
||||
auto historyScrollNone = std::unique_ptr<HistoryScrollNone>(new HistoryScrollNone());
|
||||
QCOMPARE(historyScrollNone->getMaxLines(), 0);
|
||||
QCOMPARE(historyScrollNone->reflowLines(10), 0);
|
||||
|
||||
// Compact
|
||||
auto compactHistoryScroll = std::unique_ptr<CompactHistoryScroll>(new CompactHistoryScroll(10));
|
||||
|
||||
QCOMPARE(compactHistoryScroll->getMaxLines(), 10);
|
||||
compactHistoryScroll->addCells(testImage, testStringSize);
|
||||
compactHistoryScroll->addLine();
|
||||
QCOMPARE(compactHistoryScroll->getLines(), 1);
|
||||
QCOMPARE(compactHistoryScroll->reflowLines(10), 0);
|
||||
QCOMPARE(compactHistoryScroll->getLines(), 4);
|
||||
QCOMPARE(compactHistoryScroll->reflowLines(1), 26);
|
||||
QCOMPARE(compactHistoryScroll->getLines(), 10);
|
||||
QCOMPARE(compactHistoryScroll->getLineLen(5), 1);
|
||||
compactHistoryScroll->getCells(3, 0, 1, &testChar);
|
||||
QCOMPARE(testChar, testImage[testStringSize - 7]);
|
||||
compactHistoryScroll->getCells(0, 0, 1, &testChar);
|
||||
QCOMPARE(testChar, testImage[testStringSize - 10]);
|
||||
compactHistoryScroll->getCells(9, 0, 1, &testChar);
|
||||
QCOMPARE(testChar, testImage[testStringSize - 1]);
|
||||
|
||||
// File
|
||||
auto historyScrollFile = std::unique_ptr<HistoryScrollFile>(new HistoryScrollFile());
|
||||
|
||||
QCOMPARE(historyScrollFile->getMaxLines(), 0);
|
||||
historyScrollFile->addCells(testImage, testStringSize);
|
||||
historyScrollFile->addLine();
|
||||
QCOMPARE(historyScrollFile->getLines(), 1);
|
||||
QCOMPARE(historyScrollFile->getMaxLines(), 1);
|
||||
QCOMPARE(historyScrollFile->reflowLines(10), 0);
|
||||
QCOMPARE(historyScrollFile->getLines(), 4);
|
||||
QCOMPARE(historyScrollFile->getMaxLines(), 4);
|
||||
QCOMPARE(historyScrollFile->reflowLines(1), 0);
|
||||
QCOMPARE(historyScrollFile->getLines(), testStringSize);
|
||||
QCOMPARE(historyScrollFile->getLineLen(5), 1);
|
||||
historyScrollFile->getCells(3, 0, 1, &testChar);
|
||||
QCOMPARE(testChar, testImage[3]);
|
||||
historyScrollFile->getCells(0, 0, 1, &testChar);
|
||||
QCOMPARE(testChar, testImage[0]);
|
||||
historyScrollFile->getCells(testStringSize - 1, 0, 1, &testChar);
|
||||
QCOMPARE(testChar, testImage[testStringSize - 1]);
|
||||
}
|
||||
|
||||
void HistoryTest::testHistoryTypeChange()
|
||||
{
|
||||
std::unique_ptr<HistoryScroll> historyScroll(nullptr);
|
||||
|
||||
const char testString[] = "abcdefghijklmnopqrstuvwxyz1234567890";
|
||||
const int testStringSize = sizeof(testString) / sizeof(char) - 1;
|
||||
auto testImage = std::make_unique<Character[]>(testStringSize);
|
||||
Character testChar;
|
||||
for (int i = 0; i < testStringSize; i++) {
|
||||
testImage[i] = Character((uint)testString[i]);
|
||||
}
|
||||
|
||||
// None
|
||||
auto historyTypeNone = std::make_unique<HistoryTypeNone>();
|
||||
historyTypeNone->scroll(historyScroll);
|
||||
|
||||
// None to File
|
||||
auto historyTypeFile = std::make_unique<HistoryTypeFile>();
|
||||
historyTypeFile->scroll(historyScroll);
|
||||
|
||||
historyScroll->addCells(testImage.get(), testStringSize);
|
||||
historyScroll->addLine();
|
||||
QCOMPARE(historyScroll->reflowLines(1), 0);
|
||||
QCOMPARE(historyScroll->getLines(), testStringSize);
|
||||
historyScroll->getCells(0, 0, 1, &testChar);
|
||||
QCOMPARE(testChar, testImage[0]);
|
||||
|
||||
// File to Compact
|
||||
auto compactHistoryType = std::make_unique<CompactHistoryType>(10);
|
||||
compactHistoryType->scroll(historyScroll);
|
||||
|
||||
QCOMPARE(historyScroll->getLines(), 10);
|
||||
historyScroll->getCells(0, 0, 1, &testChar);
|
||||
QCOMPARE(testChar, testImage[testStringSize - 10]);
|
||||
|
||||
// Compact to File
|
||||
historyTypeFile->scroll(historyScroll);
|
||||
QCOMPARE(historyScroll->getLines(), 10);
|
||||
historyScroll->getCells(0, 0, 1, &testChar);
|
||||
QCOMPARE(testChar, testImage[testStringSize - 10]);
|
||||
|
||||
// File to None
|
||||
historyTypeNone->scroll(historyScroll);
|
||||
QCOMPARE(historyScroll->getLines(), 0);
|
||||
}
|
||||
|
||||
QTEST_MAIN(HistoryTest)
|
||||
|
||||
#include "moc_HistoryTest.cpp"
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Kurt Hindenburg <kurt.hindenburg@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef HISTORYTEST_H
|
||||
#define HISTORYTEST_H
|
||||
|
||||
#include <kde_terminal_interface.h>
|
||||
|
||||
#include "../characters/Character.h"
|
||||
#include "../history/HistoryScrollFile.h"
|
||||
#include "../history/HistoryScrollNone.h"
|
||||
#include "../history/HistoryTypeFile.h"
|
||||
#include "../history/HistoryTypeNone.h"
|
||||
#include "../history/compact/CompactHistoryScroll.h"
|
||||
#include "../history/compact/CompactHistoryType.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class HistoryTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void testHistoryNone();
|
||||
void testHistoryFile();
|
||||
void testCompactHistory();
|
||||
void testEmulationHistory();
|
||||
void testHistoryScroll();
|
||||
void testHistoryReflow();
|
||||
void testHistoryTypeChange();
|
||||
|
||||
private:
|
||||
static constexpr const char testString[] = "abcdefghijklmnopqrstuvwxyz1234567890";
|
||||
static constexpr const int testStringSize = sizeof(testString) / sizeof(char) - 1;
|
||||
Character *testImage = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HISTORYTEST_H
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Ahmad Samir <a.samirh78@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "HotSpotFilterTest.h"
|
||||
#include <QTest>
|
||||
|
||||
QTEST_GUILESS_MAIN(HotSpotFilterTest)
|
||||
|
||||
void HotSpotFilterTest::testUrlFilterRegex_data()
|
||||
{
|
||||
QTest::addColumn<QString>("url");
|
||||
QTest::addColumn<QString>("expectedUrl");
|
||||
QTest::addColumn<bool>("matchResult");
|
||||
|
||||
// A space, \n, or \t before the url to match what happens at runtime,
|
||||
// i.e. to match "http" but not "foohttp"
|
||||
QTest::newRow("url_simple") << " https://api.kde.org"
|
||||
<< "https://api.kde.org" << true;
|
||||
QTest::newRow("url_with_port") << "\nhttps://api.kde.org:2098"
|
||||
<< "https://api.kde.org:2098" << true;
|
||||
QTest::newRow("url_with_port_trailing_slash") << "\nhttps://api.kde.org:2098/"
|
||||
<< "https://api.kde.org:2098/" << true;
|
||||
QTest::newRow("url_with_numeric_host") << "\nhttp://127.0.0.1"
|
||||
<< "http://127.0.0.1" << true;
|
||||
QTest::newRow("url_with_numeric_host_port") << "\nhttp://127.0.0.1:4000"
|
||||
<< "http://127.0.0.1:4000" << true;
|
||||
QTest::newRow("url_with_numeric_host_port_slash") << "\nhttp://127.0.0.1:4000/"
|
||||
<< "http://127.0.0.1:4000/" << true;
|
||||
QTest::newRow("url_with_path") << "https://api.kde.org/path/to/somewhere"
|
||||
<< "https://api.kde.org/path/to/somewhere" << true;
|
||||
QTest::newRow("url_with_query") << "https://user:pass@api.kde.org?somequery=foo"
|
||||
<< "https://user:pass@api.kde.org?somequery=foo" << true;
|
||||
QTest::newRow("url_with_port_path") << " https://api.kde.org:2098/path/to/somewhere"
|
||||
<< "https://api.kde.org:2098/path/to/somewhere" << true;
|
||||
QTest::newRow("url_with_user_password") << "\thttps://user:blah@api.kde.org"
|
||||
<< "https://user:blah@api.kde.org" << true;
|
||||
QTest::newRow("url_with_user_password_port_fragment") << " https://user:blah@api.kde.org:2098#fragment"
|
||||
<< "https://user:blah@api.kde.org:2098#fragment" << true;
|
||||
QTest::newRow("url_all_bells") << " https://user:pass@api.kde.org:2098/path/to/somewhere?somequery=foo#fragment"
|
||||
<< "https://user:pass@api.kde.org:2098/path/to/somewhere?somequery=foo#fragment" << true;
|
||||
QTest::newRow("uppercase") << " https://invent.kde.org/frameworks/ktexteditor/-/blob/master/README.md"
|
||||
<< "https://invent.kde.org/frameworks/ktexteditor/-/blob/master/README.md" << true;
|
||||
QTest::newRow("markup") << " [https://foobar](https://foobar)"
|
||||
<< "https://foobar" << true;
|
||||
QTest::newRow("markup_parens") << "[unix-history-repo](https://github.com/dspinellis/unix-history-repo)"
|
||||
<< "https://github.com/dspinellis/unix-history-repo" << true;
|
||||
QTest::newRow("markup_with_parens_inside_parens") << "[*Das verrückte Labyrinth*](https://en.wikipedia.org/wiki/Labyrinth_(board_game))"
|
||||
<< "https://en.wikipedia.org/wiki/Labyrinth_(board_game)" << true;
|
||||
|
||||
QTest::newRow("bracket_before") << "[198]http://www.ietf.org/rfc/rfc2396.txt"
|
||||
<< "http://www.ietf.org/rfc/rfc2396.txt" << true;
|
||||
QTest::newRow("quote_before") << "\"http://www.ietf.org/rfc/rfc2396.txt"
|
||||
<< "http://www.ietf.org/rfc/rfc2396.txt" << true;
|
||||
|
||||
QTest::newRow("grave_before") << "`https://foo.bar`"
|
||||
<< "https://foo.bar" << true;
|
||||
|
||||
QTest::newRow("equals_before") << "foo=https://foo.bar"
|
||||
<< "https://foo.bar" << true;
|
||||
|
||||
QTest::newRow("url_inside_angle_brackets") << "<https://google.com>"
|
||||
<< "https://google.com" << true;
|
||||
|
||||
QTest::newRow("file_scheme") << "file:///some/file"
|
||||
<< "file:///some/file" << true;
|
||||
|
||||
QTest::newRow("uppercase_host") << "https://EXAMPLE.com"
|
||||
<< "https://EXAMPLE.com" << true;
|
||||
QTest::newRow("uppercase_query") << "https://example.com?fooOpt=barVal"
|
||||
<< "https://example.com?fooOpt=barVal" << true;
|
||||
QTest::newRow("uppercase_fragment") << "https://example.com?fooOpt=barVal#FRAG"
|
||||
<< "https://example.com?fooOpt=barVal#FRAG" << true;
|
||||
|
||||
QTest::newRow("www") << " www.kde.org"
|
||||
<< "www.kde.org" << true;
|
||||
|
||||
QTest::newRow("with_comma_in_path") << "https://example.com/foo,bar"
|
||||
<< "https://example.com/foo,bar" << true;
|
||||
|
||||
QTest::newRow("empty_query") << "http://example.com/?"
|
||||
<< "http://example.com/?" << true;
|
||||
QTest::newRow("empty_fragment") << "http://example.com/#"
|
||||
<< "http://example.com/#" << true;
|
||||
|
||||
QTest::newRow("www_followed_by_colon") << "www.example.com:foo@bar.com"
|
||||
<< "www.example.com" << true;
|
||||
|
||||
QTest::newRow("ipv6") << "http://[2a00:1450:4001:829::200e]/"
|
||||
<< "http://[2a00:1450:4001:829::200e]/" << true;
|
||||
QTest::newRow("ipv6_with_port") << "http://[2a00:1450:4001:829::200e]:80/"
|
||||
<< "http://[2a00:1450:4001:829::200e]:80/" << true;
|
||||
|
||||
QTest::newRow("query_with_question_marks") << "ldap://[2001:db8::7]/c=GB?objectClass?one"
|
||||
<< "ldap://[2001:db8::7]/c=GB?objectClass?one" << true;
|
||||
|
||||
QTest::newRow("path_with_parens") << "https://en.wikipedia.org/wiki/C_(programming_language)"
|
||||
<< "https://en.wikipedia.org/wiki/C_(programming_language)" << true;
|
||||
QTest::newRow("query_with_parens") << "http://en.wikipedia.org/w/index.php?title=Thresholding_(image_processing)&oldid=132306976"
|
||||
<< "http://en.wikipedia.org/w/index.php?title=Thresholding_(image_processing)&oldid=132306976" << true;
|
||||
QTest::newRow("fragment_with_parens") << "https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences"
|
||||
<< "https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences" << true;
|
||||
QTest::newRow("url_with_lots_of_parens") << "(https://example.com/foo(bar(baz(qux)quux)quuux))))"
|
||||
<< "https://example.com/foo(bar(baz(qux)quux)quuux)" << true;
|
||||
}
|
||||
|
||||
void HotSpotFilterTest::testUrlFilterRegex()
|
||||
{
|
||||
QFETCH(QString, url);
|
||||
QFETCH(QString, expectedUrl);
|
||||
QFETCH(bool, matchResult);
|
||||
|
||||
const QRegularExpression ®ex = Konsole::UrlFilter::FullUrlRegExp;
|
||||
const QRegularExpressionMatch match = regex.match(url);
|
||||
// qDebug() << match;
|
||||
|
||||
QCOMPARE(match.hasMatch(), matchResult);
|
||||
if (matchResult) {
|
||||
QCOMPARE(match.capturedView(0), expectedUrl);
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_HotSpotFilterTest.cpp"
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Ahmad Samir <a.samirh78@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef HOTSPOTFILTERTEST_H
|
||||
#define HOTSPOTFILTERTEST_H
|
||||
|
||||
#include "filterHotSpots/UrlFilter.h"
|
||||
|
||||
class HotSpotFilterTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void testUrlFilterRegex_data();
|
||||
void testUrlFilterRegex();
|
||||
};
|
||||
|
||||
#endif // HOTSPOTFILTERTEST_H
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013, 2018 Kurt Hindenburg <kurt.hindenburg@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "KeyboardTranslatorTest.h"
|
||||
|
||||
#include "keyboardtranslator/FallbackKeyboardTranslator.h"
|
||||
#include "keyboardtranslator/KeyboardTranslatorReader.h"
|
||||
|
||||
// KDE
|
||||
#include <QTest>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
Q_DECLARE_METATYPE(Qt::KeyboardModifiers)
|
||||
|
||||
void KeyboardTranslatorTest::testEntryTextWildcards_data()
|
||||
{
|
||||
// Shift = 1 + (1 << 0) = 2
|
||||
// Alt = 1 + (1 << 2) = 3
|
||||
// Control = 1 + (1 << 4) = 5
|
||||
|
||||
QTest::addColumn<QByteArray>("text");
|
||||
QTest::addColumn<QByteArray>("result");
|
||||
QTest::addColumn<bool>("wildcards");
|
||||
QTest::addColumn<Qt::KeyboardModifiers>("modifiers");
|
||||
|
||||
QTest::newRow("Home no wildcards no modifiers") << QByteArray("Home") << QByteArray("Home") << false << Qt::KeyboardModifiers(Qt::NoModifier);
|
||||
QTest::newRow("Home no wildcards Shift modifiers") << QByteArray("Home") << QByteArray("Home") << false << Qt::KeyboardModifiers(Qt::ShiftModifier);
|
||||
QTest::newRow("Home no wildcards Alt modifiers") << QByteArray("Home") << QByteArray("Home") << false << Qt::KeyboardModifiers(Qt::AltModifier);
|
||||
QTest::newRow("Home no wildcards Control modifiers") << QByteArray("Home") << QByteArray("Home") << false << Qt::KeyboardModifiers(Qt::ControlModifier);
|
||||
|
||||
QTest::newRow("Home yes wildcards no modifiers") << QByteArray("Home") << QByteArray("Home") << true << Qt::KeyboardModifiers(Qt::NoModifier);
|
||||
QTest::newRow("Home yes wildcards Shift modifiers") << QByteArray("Home") << QByteArray("Home") << true << Qt::KeyboardModifiers(Qt::ShiftModifier);
|
||||
QTest::newRow("Home yes wildcards Alt modifiers") << QByteArray("Home") << QByteArray("Home") << true << Qt::KeyboardModifiers(Qt::AltModifier);
|
||||
QTest::newRow("Home yes wildcards Control modifiers") << QByteArray("Home") << QByteArray("Home") << true << Qt::KeyboardModifiers(Qt::ControlModifier);
|
||||
|
||||
// text, results: no mod, shift, alt, control
|
||||
QList<QByteArray> entry;
|
||||
entry << QByteArray("E*") << QByteArray("E1") << QByteArray("E2") << QByteArray("E3") << QByteArray("E5");
|
||||
QTest::newRow("E* yes wildcards no modifiers") << entry[0] << entry[1] << true << Qt::KeyboardModifiers(Qt::NoModifier);
|
||||
QTest::newRow("E* yes wildcards Shift modifiers") << entry[0] << entry[2] << true << Qt::KeyboardModifiers(Qt::ShiftModifier);
|
||||
QTest::newRow("E* yes wildcards Alt modifiers") << entry[0] << entry[3] << true << Qt::KeyboardModifiers(Qt::AltModifier);
|
||||
QTest::newRow("E* yes wildcards Control modifiers") << entry[0] << entry[4] << true << Qt::KeyboardModifiers(Qt::ControlModifier);
|
||||
|
||||
// combinations
|
||||
entry.clear();
|
||||
entry << QByteArray("E*") << QByteArray("E4") << QByteArray("E6") << QByteArray("E8") << QByteArray("E7");
|
||||
QTest::newRow("E* yes wildcards Shift+Alt modifiers") << entry[0] << entry[1] << true << Qt::KeyboardModifiers(Qt::ShiftModifier | Qt::AltModifier);
|
||||
QTest::newRow("E* yes wildcards Shift+Control modifiers") << entry[0] << entry[2] << true << Qt::KeyboardModifiers(Qt::ShiftModifier | Qt::ControlModifier);
|
||||
QTest::newRow("E* yes wildcards Shift+Alt+Control modifiers")
|
||||
<< entry[0] << entry[3] << true << Qt::KeyboardModifiers(Qt::ShiftModifier | Qt::AltModifier | Qt::ControlModifier);
|
||||
QTest::newRow("E* yes wildcards Alt+Control modifiers") << entry[0] << entry[4] << true << Qt::KeyboardModifiers(Qt::AltModifier | Qt::ControlModifier);
|
||||
|
||||
// text, results: no mod, shift, alt, control
|
||||
entry.clear();
|
||||
entry << QByteArray("\033[24;*~") << QByteArray("\033[24;1~") << QByteArray("\033[24;2~") << QByteArray("\033[24;3~") << QByteArray("\033[24;5~");
|
||||
QTest::newRow("\033[24;*~ yes wildcards no modifiers") << entry[0] << entry[1] << true << Qt::KeyboardModifiers(Qt::NoModifier);
|
||||
QTest::newRow("\033[24;*~ yes wildcards Shift modifiers") << entry[0] << entry[2] << true << Qt::KeyboardModifiers(Qt::ShiftModifier);
|
||||
QTest::newRow("\033[24;*~ yes wildcards Alt modifiers") << entry[0] << entry[3] << true << Qt::KeyboardModifiers(Qt::AltModifier);
|
||||
QTest::newRow("\033[24;*~ yes wildcards Control modifiers") << entry[0] << entry[4] << true << Qt::KeyboardModifiers(Qt::ControlModifier);
|
||||
|
||||
// combinations
|
||||
entry.clear();
|
||||
entry << QByteArray("\033[24;*~") << QByteArray("\033[24;4~") << QByteArray("\033[24;6~") << QByteArray("\033[24;8~") << QByteArray("\033[24;7~");
|
||||
QTest::newRow("\033[24;*~ yes wildcards Shift+Alt modifiers") << entry[0] << entry[1] << true << Qt::KeyboardModifiers(Qt::ShiftModifier | Qt::AltModifier);
|
||||
QTest::newRow("\033[24;*~ yes wildcards Shift+Control modifiers")
|
||||
<< entry[0] << entry[2] << true << Qt::KeyboardModifiers(Qt::ShiftModifier | Qt::ControlModifier);
|
||||
QTest::newRow("\033[24;*~ yes wildcards Shift+Alt+Control modifiers")
|
||||
<< entry[0] << entry[3] << true << Qt::KeyboardModifiers(Qt::ShiftModifier | Qt::AltModifier | Qt::ControlModifier);
|
||||
QTest::newRow("\033[24;*~ yes wildcards Alt+Control modifiers")
|
||||
<< entry[0] << entry[4] << true << Qt::KeyboardModifiers(Qt::AltModifier | Qt::ControlModifier);
|
||||
}
|
||||
|
||||
void KeyboardTranslatorTest::testEntryTextWildcards()
|
||||
{
|
||||
QFETCH(QByteArray, text);
|
||||
QFETCH(QByteArray, result);
|
||||
QFETCH(bool, wildcards);
|
||||
QFETCH(Qt::KeyboardModifiers, modifiers);
|
||||
|
||||
KeyboardTranslator::Entry entry;
|
||||
entry.setText(text);
|
||||
|
||||
QCOMPARE(entry.text(wildcards, modifiers), result);
|
||||
}
|
||||
|
||||
// Use FallbackKeyboardTranslator to test basic functionality
|
||||
void KeyboardTranslatorTest::testFallback()
|
||||
{
|
||||
auto fallback = std::unique_ptr<FallbackKeyboardTranslator>(new FallbackKeyboardTranslator());
|
||||
|
||||
QCOMPARE(QStringLiteral("fallback"), fallback->name());
|
||||
QCOMPARE(QStringLiteral("Fallback Keyboard Translator"), fallback->description());
|
||||
|
||||
auto entries = fallback->entries();
|
||||
QCOMPARE(1, entries.size());
|
||||
auto entry = fallback->findEntry(Qt::Key_Tab, Qt::NoModifier);
|
||||
QVERIFY(!entry.isNull());
|
||||
QCOMPARE(FallbackKeyboardTranslator::Command::NoCommand, entry.command());
|
||||
QCOMPARE(int(Qt::Key_Tab), entry.keyCode());
|
||||
QCOMPARE(QByteArray("\t"), entry.text());
|
||||
QCOMPARE(QByteArray("\\t"), entry.escapedText());
|
||||
QCOMPARE(Qt::KeyboardModifiers(Qt::NoModifier), entry.modifiers());
|
||||
QCOMPARE(Qt::KeyboardModifiers(Qt::NoModifier), entry.modifierMask());
|
||||
QCOMPARE(KeyboardTranslator::States(KeyboardTranslator::NoState), entry.state());
|
||||
QCOMPARE(QStringLiteral("Tab"), entry.conditionToString());
|
||||
QCOMPARE(QStringLiteral("\\t"), entry.resultToString());
|
||||
QVERIFY(entry.matches(Qt::Key_Tab, Qt::NoModifier, KeyboardTranslator::NoState));
|
||||
QVERIFY(entry == fallback->findEntry(Qt::Key_Tab, Qt::NoModifier));
|
||||
}
|
||||
|
||||
void KeyboardTranslatorTest::testHexKeys()
|
||||
{
|
||||
QFile linuxkeytab(QFINDTESTDATA(QStringLiteral("data/test.keytab")));
|
||||
QVERIFY(linuxkeytab.exists());
|
||||
QVERIFY(linuxkeytab.open(QIODevice::ReadOnly));
|
||||
|
||||
auto translator = std::unique_ptr<KeyboardTranslator>(new KeyboardTranslator(QStringLiteral("testtranslator")));
|
||||
|
||||
KeyboardTranslatorReader reader(&linuxkeytab);
|
||||
while (reader.hasNextEntry()) {
|
||||
translator->addEntry(reader.nextEntry());
|
||||
}
|
||||
linuxkeytab.close();
|
||||
|
||||
// A worthless check ATM
|
||||
if (reader.parseError()) {
|
||||
QFAIL("Parse failure");
|
||||
}
|
||||
|
||||
QCOMPARE(QStringLiteral("testtranslator"), translator->name());
|
||||
QCOMPARE(QString(), translator->description());
|
||||
|
||||
auto entry = translator->findEntry(Qt::Key_Backspace, Qt::NoModifier);
|
||||
QVERIFY(!entry.isNull());
|
||||
QCOMPARE(FallbackKeyboardTranslator::Command::NoCommand, entry.command());
|
||||
QCOMPARE(int(Qt::Key_Backspace), entry.keyCode());
|
||||
QCOMPARE(QByteArray("\x7F"), entry.text());
|
||||
QCOMPARE(QByteArray("\\x7f"), entry.escapedText());
|
||||
QCOMPARE(Qt::KeyboardModifiers(Qt::NoModifier), entry.modifiers());
|
||||
QCOMPARE(Qt::KeyboardModifiers(Qt::NoModifier), entry.modifierMask());
|
||||
QCOMPARE(KeyboardTranslator::States(KeyboardTranslator::NoState), entry.state());
|
||||
QCOMPARE(QStringLiteral("Backspace"), entry.conditionToString());
|
||||
QCOMPARE(QStringLiteral("\\x7f"), entry.resultToString());
|
||||
QVERIFY(entry.matches(Qt::Key_Backspace, Qt::NoModifier, KeyboardTranslator::NoState));
|
||||
QVERIFY(entry == translator->findEntry(Qt::Key_Backspace, Qt::NoModifier));
|
||||
|
||||
entry = translator->findEntry(Qt::Key_Delete, Qt::NoModifier);
|
||||
QVERIFY(!entry.isNull());
|
||||
QCOMPARE(FallbackKeyboardTranslator::Command::NoCommand, entry.command());
|
||||
QCOMPARE(int(Qt::Key_Delete), entry.keyCode());
|
||||
QCOMPARE(QByteArray("\x08"), entry.text());
|
||||
QCOMPARE(QByteArray("\\b"), entry.escapedText());
|
||||
QCOMPARE(Qt::KeyboardModifiers(Qt::NoModifier), entry.modifiers());
|
||||
QCOMPARE(Qt::KeyboardModifiers(Qt::NoModifier), entry.modifierMask());
|
||||
QCOMPARE(KeyboardTranslator::States(KeyboardTranslator::NoState), entry.state());
|
||||
QCOMPARE(QStringLiteral("Del"), entry.conditionToString());
|
||||
QCOMPARE(QStringLiteral("\\b"), entry.resultToString());
|
||||
QVERIFY(entry.matches(Qt::Key_Delete, Qt::NoModifier, KeyboardTranslator::NoState));
|
||||
QVERIFY(!entry.matches(Qt::Key_Backspace, Qt::NoModifier, KeyboardTranslator::NoState));
|
||||
QVERIFY(!(entry == translator->findEntry(Qt::Key_Backspace, Qt::NoModifier)));
|
||||
|
||||
entry = translator->findEntry(Qt::Key_Space, Qt::NoModifier);
|
||||
QVERIFY(!entry.isNull());
|
||||
QCOMPARE(FallbackKeyboardTranslator::Command::NoCommand, entry.command());
|
||||
QCOMPARE(int(Qt::Key_Space), entry.keyCode());
|
||||
QEXPECT_FAIL("", "Several keytabs use x00 as Space +Control; text() fails", Continue);
|
||||
QCOMPARE(QByteArray("\x00"), entry.text());
|
||||
QCOMPARE(QByteArray("\\x00"), entry.escapedText());
|
||||
QCOMPARE(Qt::KeyboardModifiers(Qt::NoModifier), entry.modifiers());
|
||||
QCOMPARE(Qt::KeyboardModifiers(Qt::NoModifier), entry.modifierMask());
|
||||
QCOMPARE(KeyboardTranslator::States(KeyboardTranslator::NoState), entry.state());
|
||||
QCOMPARE(QStringLiteral("Space"), entry.conditionToString());
|
||||
QCOMPARE(QStringLiteral("\\x00"), entry.resultToString());
|
||||
QVERIFY(entry.matches(Qt::Key_Space, Qt::NoModifier, KeyboardTranslator::NoState));
|
||||
QVERIFY(entry == translator->findEntry(Qt::Key_Space, Qt::NoModifier));
|
||||
QVERIFY(!entry.matches(Qt::Key_Backspace, Qt::NoModifier, KeyboardTranslator::NoState));
|
||||
QVERIFY(!(entry == translator->findEntry(Qt::Key_Backspace, Qt::NoModifier)));
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(KeyboardTranslatorTest)
|
||||
|
||||
#include "moc_KeyboardTranslatorTest.cpp"
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Kurt Hindenburg <kurt.hindenburg@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KEYBOARDTRANSLATORTEST_H
|
||||
#define KEYBOARDTRANSLATORTEST_H
|
||||
|
||||
#include "keyboardtranslator/KeyboardTranslator.h"
|
||||
#include <QObject>
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class KeyboardTranslatorTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void testEntryTextWildcards();
|
||||
void testEntryTextWildcards_data();
|
||||
void testFallback();
|
||||
void testHexKeys();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // KEYBOARDTRANSLATORTEST_H
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "PartTest.h"
|
||||
|
||||
// Qt
|
||||
#include <QDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
// KDE
|
||||
#include <KPluginFactory>
|
||||
#include <KPtyDevice>
|
||||
#include <KPtyProcess>
|
||||
#include <QTest>
|
||||
|
||||
// Konsole
|
||||
#include "../Pty.h"
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
void PartTest::initTestCase()
|
||||
{
|
||||
/* Try to test against build konsolepart, so move directory containing
|
||||
executable to front of libraryPaths. KPluginLoader should find the
|
||||
part first in the build dir over the system installed ones.
|
||||
I believe the CI installs first and then runs the test so the other
|
||||
paths can not be removed.
|
||||
*/
|
||||
const auto libraryPaths = QCoreApplication::libraryPaths();
|
||||
auto buildPath = libraryPaths.last();
|
||||
QCoreApplication::removeLibraryPath(buildPath);
|
||||
// konsolepart.so is in ../autotests/
|
||||
if (buildPath.endsWith(QStringLiteral("/autotests"))) {
|
||||
buildPath.chop(10);
|
||||
}
|
||||
QCoreApplication::addLibraryPath(buildPath);
|
||||
}
|
||||
|
||||
void PartTest::testFdShell()
|
||||
{
|
||||
#if defined(Q_OS_FREEBSD)
|
||||
QSKIP("Skipping on CI FreeBSD14_qt6x", SkipSingle);
|
||||
return;
|
||||
#endif
|
||||
|
||||
testFd(true);
|
||||
}
|
||||
|
||||
void PartTest::testFdStandalone()
|
||||
{
|
||||
testFd(false);
|
||||
}
|
||||
|
||||
void PartTest::testFd(bool runShell)
|
||||
{
|
||||
// find ping
|
||||
QStringList pingList;
|
||||
QFileInfo info;
|
||||
QString pingExe;
|
||||
pingList << QStringLiteral("/bin/ping") << QStringLiteral("/sbin/ping");
|
||||
for (int i = 0; i < pingList.size(); ++i) {
|
||||
info.setFile(pingList.at(i));
|
||||
if (info.exists() && info.isExecutable()) {
|
||||
pingExe = pingList.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (pingExe.isEmpty()) {
|
||||
QSKIP("ping command not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// create a Konsole part and attempt to connect to it
|
||||
KParts::Part *terminalPart = createPart();
|
||||
if (terminalPart == nullptr) { // not found
|
||||
QFAIL("konsolepart not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// start a pty process
|
||||
KPtyProcess ptyProcess;
|
||||
ptyProcess.setProgram(pingExe, QStringList() << QStringLiteral("localhost"));
|
||||
ptyProcess.setPtyChannels(KPtyProcess::AllChannels);
|
||||
ptyProcess.start();
|
||||
QVERIFY(ptyProcess.waitForStarted());
|
||||
|
||||
int fd = ptyProcess.pty()->masterFd();
|
||||
|
||||
// test that the 2nd argument of openTeletype is optional,
|
||||
// to run without shell
|
||||
if (runShell) {
|
||||
// connect to an existing pty
|
||||
bool result = QMetaObject::invokeMethod(terminalPart, "openTeletype", Qt::DirectConnection, Q_ARG(int, fd));
|
||||
QVERIFY(result);
|
||||
} else {
|
||||
// test the optional 2nd argument of openTeletype, to run without shell
|
||||
bool result = QMetaObject::invokeMethod(terminalPart, "openTeletype", Qt::DirectConnection, Q_ARG(int, fd), Q_ARG(bool, false));
|
||||
QVERIFY(result);
|
||||
}
|
||||
|
||||
// suspend the KPtyDevice so that the embedded terminal gets a chance to
|
||||
// read from the pty. Otherwise the KPtyDevice will simply read everything
|
||||
// as soon as it becomes available and the terminal will not display any output
|
||||
ptyProcess.pty()->setSuspended(true);
|
||||
|
||||
QPointer<QDialog> dialog = new QDialog();
|
||||
auto layout = new QVBoxLayout(dialog.data());
|
||||
auto explanation = runShell ? QStringLiteral("Output of 'ping localhost' should appear in a terminal below for 5 seconds")
|
||||
: QStringLiteral("Output of 'ping localhost' should appear standalone below for 5 seconds");
|
||||
layout->addWidget(new QLabel(explanation));
|
||||
layout->addWidget(terminalPart->widget());
|
||||
QTimer::singleShot(5000, dialog.data(), &QDialog::close);
|
||||
dialog.data()->exec();
|
||||
|
||||
delete terminalPart;
|
||||
delete dialog.data();
|
||||
ptyProcess.kill();
|
||||
ptyProcess.waitForFinished(1000);
|
||||
}
|
||||
|
||||
KParts::Part *PartTest::createPart()
|
||||
{
|
||||
const KPluginMetaData metaData(QStringLiteral("konsolepart"));
|
||||
Q_ASSERT(metaData.isValid());
|
||||
|
||||
KPluginFactory::Result<KParts::Part> result = KPluginFactory::instantiatePlugin<KParts::Part>(metaData, this);
|
||||
Q_ASSERT(result);
|
||||
|
||||
return result.plugin;
|
||||
}
|
||||
|
||||
QTEST_MAIN(PartTest)
|
||||
|
||||
#include "moc_PartTest.cpp"
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef PARTTEST_H
|
||||
#define PARTTEST_H
|
||||
|
||||
#include <KParts/Part>
|
||||
#include <kde_terminal_interface.h>
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class PartTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void testFdShell();
|
||||
void testFdStandalone();
|
||||
|
||||
private:
|
||||
void testFd(bool runShell);
|
||||
KParts::Part *createPart();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // PARTTEST_H
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Theodore Wang <theodorewang12@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "ProcessInfoTest.h"
|
||||
|
||||
// Qt
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
#include <QTest>
|
||||
|
||||
// Konsole
|
||||
#include "../ProcessInfo.h"
|
||||
#include "../session/Session.h"
|
||||
|
||||
// Others
|
||||
#include <memory>
|
||||
|
||||
#include <QTest>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
std::unique_ptr<ProcessInfo> ProcessInfoTest::createProcInfo(const KProcess &proc)
|
||||
{
|
||||
return std::unique_ptr<ProcessInfo>(ProcessInfo::newInstance(proc.processId()));
|
||||
}
|
||||
|
||||
void ProcessInfoTest::testProcessValidity()
|
||||
{
|
||||
if (Session::checkProgram(QStringLiteral("bash")).isEmpty())
|
||||
return;
|
||||
|
||||
KProcess proc;
|
||||
proc.setProgram(QStringLiteral("bash"));
|
||||
proc.start();
|
||||
|
||||
QVERIFY(createProcInfo(proc)->isValid());
|
||||
|
||||
proc.close();
|
||||
proc.waitForFinished(1000);
|
||||
}
|
||||
|
||||
void ProcessInfoTest::testProcessCwd()
|
||||
{
|
||||
#ifndef Q_OS_FREEBSD
|
||||
if (Session::checkProgram(QStringLiteral("bash")).isEmpty())
|
||||
return;
|
||||
|
||||
KProcess proc;
|
||||
proc.setProgram({QStringLiteral("bash"), QStringLiteral("-x")});
|
||||
proc.start();
|
||||
|
||||
auto procInfo = createProcInfo(proc);
|
||||
const QString startDir(QDir::currentPath());
|
||||
const QString parentDir(startDir.mid(0, startDir.lastIndexOf(QLatin1Char('/'))));
|
||||
|
||||
bool ok;
|
||||
QString currDir;
|
||||
|
||||
currDir = procInfo->currentDir(&ok);
|
||||
QVERIFY(ok);
|
||||
QCOMPARE(currDir, startDir);
|
||||
|
||||
proc.write(QStringLiteral("cd ..\n").toLocal8Bit());
|
||||
proc.waitForReadyRead(1000);
|
||||
procInfo->update();
|
||||
|
||||
currDir = procInfo->currentDir(&ok);
|
||||
QVERIFY(ok);
|
||||
QCOMPARE(currDir, parentDir);
|
||||
|
||||
proc.write(QStringLiteral("exit\n").toLocal8Bit());
|
||||
proc.waitForFinished(1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ProcessInfoTest::testProcessNameSpecialChars()
|
||||
{
|
||||
#ifndef Q_OS_FREEBSD
|
||||
if (Session::checkProgram(QStringLiteral("bash")).isEmpty())
|
||||
return;
|
||||
|
||||
const QVector<QString> specNames({QStringLiteral("(( a("), QStringLiteral("("), QStringLiteral("ab) ("), QStringLiteral(")")});
|
||||
|
||||
KProcess mainProc;
|
||||
mainProc.setProgram({QStringLiteral("bash"), QStringLiteral("-x")});
|
||||
mainProc.start();
|
||||
|
||||
auto mainProcInfo = createProcInfo(mainProc);
|
||||
bool ok;
|
||||
|
||||
for (auto specName : specNames) {
|
||||
mainProc.write(QStringLiteral("cp $(which bash) '%1'\n").arg(specName).toLocal8Bit());
|
||||
mainProc.waitForReadyRead(1000);
|
||||
mainProc.write(QStringLiteral("exec %1'%2'\n").arg(QDir::currentPath() + QDir::separator(), specName).toLocal8Bit());
|
||||
mainProc.waitForReadyRead(1000);
|
||||
|
||||
mainProcInfo->update();
|
||||
|
||||
QDir::current().remove(specName);
|
||||
|
||||
const QString currName(mainProcInfo->name(&ok));
|
||||
QVERIFY(ok);
|
||||
QCOMPARE(currName, specName);
|
||||
}
|
||||
|
||||
mainProc.write(QStringLiteral("exit\n").toLocal8Bit());
|
||||
mainProc.waitForFinished(1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
QTEST_MAIN(ProcessInfoTest)
|
||||
|
||||
#include "moc_ProcessInfoTest.cpp"
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Theodore Wang <theodorewang12@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef PROCESSINFOTEST_H
|
||||
#define PROCESSINFOTEST_H
|
||||
|
||||
// Others
|
||||
#include <KProcess>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
|
||||
class ProcessInfo;
|
||||
|
||||
class ProcessInfoTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
// testing of ProcessInfo::update is itnegrated into the tests
|
||||
void testProcessValidity();
|
||||
void testProcessCwd();
|
||||
void testProcessNameSpecialChars();
|
||||
|
||||
private:
|
||||
std::unique_ptr<ProcessInfo> createProcInfo(const KProcess &proc);
|
||||
};
|
||||
|
||||
} // PROCESSINFOTEST_H
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "ProfileTest.h"
|
||||
|
||||
// Qt
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QStandardPaths>
|
||||
#include <QTemporaryFile>
|
||||
#include <QTest>
|
||||
#include <QTextStream>
|
||||
|
||||
// KDE
|
||||
|
||||
// Konsole
|
||||
#include "../profile/Profile.h"
|
||||
#include "../profile/ProfileGroup.h"
|
||||
#include "../profile/ProfileManager.cpp"
|
||||
#include "../profile/ProfileReader.h"
|
||||
#include "../profile/ProfileWriter.h"
|
||||
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include <array>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
void ProfileTest::initTestCase()
|
||||
{
|
||||
QStandardPaths::setTestModeEnabled(true);
|
||||
}
|
||||
|
||||
void ProfileTest::testProfile()
|
||||
{
|
||||
// create a new profile
|
||||
Profile *parent = new Profile;
|
||||
parent->setProperty(Profile::Name, QStringLiteral("Parent"));
|
||||
parent->setProperty(Profile::Path, QStringLiteral("FakePath"));
|
||||
|
||||
parent->setProperty(Profile::AntiAliasFonts, false);
|
||||
parent->setProperty(Profile::StartInCurrentSessionDir, false);
|
||||
|
||||
parent->setProperty(Profile::UseCustomCursorColor, true);
|
||||
QVERIFY(parent->useCustomCursorColor());
|
||||
QCOMPARE(parent->customCursorColor(), QColor());
|
||||
QCOMPARE(parent->customCursorTextColor(), QColor());
|
||||
parent->setProperty(Profile::UseCustomCursorColor, false);
|
||||
QVERIFY(!parent->useCustomCursorColor());
|
||||
QCOMPARE(parent->customCursorColor(), QColor());
|
||||
QCOMPARE(parent->customCursorTextColor(), QColor());
|
||||
|
||||
// create a child profile
|
||||
Profile *child = new Profile(Profile::Ptr(parent));
|
||||
child->setProperty(Profile::StartInCurrentSessionDir, true);
|
||||
|
||||
// check which properties are set
|
||||
QVERIFY(parent->isPropertySet(Profile::Name));
|
||||
QVERIFY(parent->isPropertySet(Profile::Path));
|
||||
QVERIFY(parent->isPropertySet(Profile::AntiAliasFonts));
|
||||
QVERIFY(!parent->isPropertySet(Profile::Icon));
|
||||
QVERIFY(!parent->isPropertySet(Profile::Command));
|
||||
QVERIFY(!parent->isPropertySet(Profile::Arguments));
|
||||
|
||||
QVERIFY(child->isPropertySet(Profile::StartInCurrentSessionDir));
|
||||
QVERIFY(!child->isPropertySet(Profile::Name));
|
||||
QVERIFY(!child->isPropertySet(Profile::AntiAliasFonts));
|
||||
QVERIFY(!child->isPropertySet(Profile::ColorScheme));
|
||||
|
||||
// read non-inheritable properties
|
||||
QCOMPARE(parent->property<QString>(Profile::Name), QStringLiteral("Parent"));
|
||||
QCOMPARE(child->property<QVariant>(Profile::Name), QVariant());
|
||||
QCOMPARE(parent->property<QString>(Profile::Path), QStringLiteral("FakePath"));
|
||||
QCOMPARE(child->property<QVariant>(Profile::Path), QVariant());
|
||||
|
||||
// read inheritable properties
|
||||
QVERIFY(!parent->property<bool>(Profile::AntiAliasFonts));
|
||||
QVERIFY(!child->property<bool>(Profile::AntiAliasFonts));
|
||||
|
||||
QVERIFY(!parent->startInCurrentSessionDir());
|
||||
QVERIFY(child->startInCurrentSessionDir());
|
||||
|
||||
delete child;
|
||||
}
|
||||
|
||||
void ProfileTest::testClone()
|
||||
{
|
||||
// create source profile and parent
|
||||
Profile::Ptr parent(new Profile);
|
||||
parent->setProperty(Profile::Command, QStringLiteral("ps"));
|
||||
parent->setProperty(Profile::ColorScheme, QStringLiteral("BlackOnWhite"));
|
||||
|
||||
Profile::Ptr source(new Profile(parent));
|
||||
source->setProperty(Profile::AntiAliasFonts, false);
|
||||
source->setProperty(Profile::HistorySize, 4567);
|
||||
|
||||
source->setProperty(Profile::Name, QStringLiteral("SourceProfile"));
|
||||
source->setProperty(Profile::Path, QStringLiteral("SourcePath"));
|
||||
|
||||
// create target to clone source and parent
|
||||
Profile::Ptr targetParent(new Profile);
|
||||
// same value as source parent
|
||||
targetParent->setProperty(Profile::Command, QStringLiteral("ps"));
|
||||
// different value from source parent
|
||||
targetParent->setProperty(Profile::ColorScheme, QStringLiteral("BlackOnGrey"));
|
||||
Profile::Ptr target(new Profile(parent));
|
||||
|
||||
// clone source profile, setting only properties that differ
|
||||
// between the source and target
|
||||
target->clone(source, true);
|
||||
|
||||
// check that properties from source have been cloned into target
|
||||
QCOMPARE(source->property<bool>(Profile::AntiAliasFonts), target->property<bool>(Profile::AntiAliasFonts));
|
||||
QCOMPARE(source->property<int>(Profile::HistorySize), target->property<int>(Profile::HistorySize));
|
||||
|
||||
// check that Name and Path properties are handled specially and not cloned
|
||||
QVERIFY(source->property<QString>(Profile::Name) != target->property<QString>(Profile::Name));
|
||||
QVERIFY(source->property<QString>(Profile::Path) != target->property<QString>(Profile::Path));
|
||||
|
||||
// check that Command property is not set in target because the values
|
||||
// are the same
|
||||
QVERIFY(!target->isPropertySet(Profile::Command));
|
||||
// check that ColorScheme property is cloned because the inherited values
|
||||
// from the source parent and target parent differ
|
||||
QCOMPARE(source->property<QString>(Profile::ColorScheme), target->property<QString>(Profile::ColorScheme));
|
||||
}
|
||||
|
||||
void ProfileTest::testProfileGroup()
|
||||
{
|
||||
// create three new profiles
|
||||
std::array<Profile::Ptr, 3> profile;
|
||||
for (auto &i : profile) {
|
||||
i = new Profile;
|
||||
QVERIFY(!i->asGroup());
|
||||
}
|
||||
|
||||
// set properties with different values
|
||||
profile[0]->setProperty(Profile::UseCustomCursorColor, true);
|
||||
profile[1]->setProperty(Profile::UseCustomCursorColor, false);
|
||||
|
||||
// set properties with same values
|
||||
for (auto &i : profile) {
|
||||
i->setProperty(Profile::HistorySize, 1234);
|
||||
}
|
||||
|
||||
// create a group profile
|
||||
ProfileGroup::Ptr group = ProfileGroup::Ptr(new ProfileGroup);
|
||||
const ProfileGroup::Ptr group_const = ProfileGroup::Ptr(new ProfileGroup);
|
||||
QVERIFY(group->asGroup());
|
||||
QVERIFY(group_const->asGroup());
|
||||
for (auto &i : profile) {
|
||||
group->addProfile(i);
|
||||
QVERIFY(group->profiles().contains(i));
|
||||
QVERIFY(!group_const->profiles().contains(i));
|
||||
}
|
||||
group->updateValues();
|
||||
|
||||
// read and check properties from the group
|
||||
QCOMPARE(group->property<int>(Profile::HistorySize), 1234);
|
||||
QCOMPARE(group_const->property<int>(Profile::HistorySize), 0);
|
||||
QCOMPARE(group->property<QVariant>(Profile::UseCustomCursorColor), QVariant());
|
||||
QCOMPARE(group_const->property<QVariant>(Profile::UseCustomCursorColor), QVariant());
|
||||
|
||||
// set and test shareable properties in the group
|
||||
group->setProperty(Profile::Command, QStringLiteral("ssh"));
|
||||
group->setProperty(Profile::AntiAliasFonts, false);
|
||||
|
||||
QCOMPARE(profile[0]->property<QString>(Profile::Command), QStringLiteral("ssh"));
|
||||
QVERIFY(!profile[1]->property<bool>(Profile::AntiAliasFonts));
|
||||
|
||||
// set and test non-shareable properties in the group
|
||||
// (should have no effect)
|
||||
group->setProperty(Profile::Name, QStringLiteral("NewName"));
|
||||
group->setProperty(Profile::Path, QStringLiteral("NewPath"));
|
||||
QVERIFY(profile[1]->property<QString>(Profile::Name) != QLatin1String("NewName"));
|
||||
QVERIFY(profile[2]->property<QString>(Profile::Path) != QLatin1String("NewPath"));
|
||||
|
||||
// remove a profile from the group
|
||||
group->removeProfile(profile[0]);
|
||||
QVERIFY(!group->profiles().contains(profile[0]));
|
||||
group->updateValues();
|
||||
|
||||
// check that profile is no longer affected by group
|
||||
group->setProperty(Profile::Command, QStringLiteral("fish"));
|
||||
QVERIFY(profile[0]->property<QString>(Profile::Command) != QLatin1String("fish"));
|
||||
}
|
||||
|
||||
// Verify the correct file name is created from the UntranslatedName
|
||||
void ProfileTest::testProfileFileNames()
|
||||
{
|
||||
Profile::Ptr profile = Profile::Ptr(new Profile);
|
||||
QFileInfo fileInfo;
|
||||
ProfileWriter writer;
|
||||
|
||||
profile->setProperty(Profile::UntranslatedName, QStringLiteral("Indiana"));
|
||||
fileInfo.setFile(writer.getPath(profile));
|
||||
QCOMPARE(fileInfo.fileName(), QStringLiteral("Indiana.profile"));
|
||||
|
||||
profile->setProperty(Profile::UntranslatedName, QStringLiteral("Old Paris"));
|
||||
fileInfo.setFile(writer.getPath(profile));
|
||||
QCOMPARE(fileInfo.fileName(), QStringLiteral("Old Paris.profile"));
|
||||
|
||||
/* FIXME: deal w/ file systems that are case-insensitive
|
||||
This leads to confusion as both Test and test can appear in the Manager
|
||||
Profile dialog while really there is only 1 test.profile file.
|
||||
Suggestions: all lowercase, testing the file system, ...
|
||||
*/
|
||||
/*
|
||||
profile->setProperty(Profile::UntranslatedName, "New Profile");
|
||||
fileInfo.setFile(writer.getPath(profile));
|
||||
QCOMPARE(fileInfo.fileName(), QString("new profile.profile"));
|
||||
*/
|
||||
|
||||
/* FIXME: don't allow certain characters in file names
|
||||
Consider: ,^@=+{}[]~!?:&*\"|#%<>$\"'();`'/\
|
||||
Suggestions: changing them all to _, just remove them, ...
|
||||
Bug 315086 comes from a user using / in the profile name - multiple
|
||||
issues there.
|
||||
*/
|
||||
/*
|
||||
profile->setProperty(Profile::UntranslatedName, "new/profile");
|
||||
fileInfo.setFile(writer.getPath(profile));
|
||||
QCOMPARE(fileInfo.fileName(), QString("new_profile.profile"));
|
||||
*/
|
||||
}
|
||||
|
||||
void ProfileTest::testProfileNameSorting()
|
||||
{
|
||||
auto *manager = ProfileManager::instance();
|
||||
|
||||
const int origCount = manager->allProfiles().size();
|
||||
|
||||
Profile::Ptr profile1 = Profile::Ptr(new Profile);
|
||||
profile1->setProperty(Profile::UntranslatedName, QStringLiteral("Indiana"));
|
||||
manager->addProfile(profile1);
|
||||
auto list = manager->allProfiles();
|
||||
int counter = 1;
|
||||
QCOMPARE(list.size(), origCount + counter++);
|
||||
// Built-in profile always at the top
|
||||
QCOMPARE(list.at(0)->name(), QStringLiteral("Built-in"));
|
||||
|
||||
QVERIFY(std::is_sorted(list.cbegin(), list.cend(), profileNameLessThan));
|
||||
|
||||
Profile::Ptr profile2 = Profile::Ptr(new Profile);
|
||||
profile2->setProperty(Profile::UntranslatedName, QStringLiteral("Old Paris"));
|
||||
manager->addProfile(profile2);
|
||||
list = manager->allProfiles();
|
||||
QCOMPARE(list.size(), origCount + counter++);
|
||||
QVERIFY(std::is_sorted(list.cbegin(), list.cend(), profileNameLessThan));
|
||||
|
||||
Profile::Ptr profile3 = Profile::Ptr(new Profile);
|
||||
profile3->setProperty(Profile::UntranslatedName, QStringLiteral("New Zealand"));
|
||||
manager->addProfile(profile3);
|
||||
list = manager->allProfiles();
|
||||
QCOMPARE(list.size(), origCount + counter++);
|
||||
QVERIFY(std::is_sorted(list.cbegin(), list.cend(), profileNameLessThan));
|
||||
|
||||
QCOMPARE(list.at(0)->name(), QStringLiteral("Built-in"));
|
||||
}
|
||||
|
||||
void ProfileTest::testBuiltinProfile()
|
||||
{
|
||||
// create a new profile
|
||||
Profile::Ptr builtin = Profile::Ptr(new Profile);
|
||||
QVERIFY(!builtin->isBuiltin());
|
||||
|
||||
builtin->useBuiltin();
|
||||
QVERIFY(builtin->isBuiltin());
|
||||
QCOMPARE(builtin->untranslatedName(), QStringLiteral("Built-in"));
|
||||
QCOMPARE(builtin->path(), QStringLiteral("FALLBACK/"));
|
||||
}
|
||||
|
||||
void ProfileTest::testLoadProfileNamedAsBuiltin()
|
||||
{
|
||||
// Create a new profile data which is literally named "Built-in".
|
||||
// New code should support loading such profiles created in older versions,
|
||||
// but new profiles must not be saved with such name.
|
||||
Profile::Ptr builtin = Profile::Ptr(new Profile);
|
||||
builtin->useBuiltin();
|
||||
|
||||
auto profileName = QFINDTESTDATA(QStringLiteral("data/named-built-in.profile"));
|
||||
QFile file(profileName);
|
||||
QVERIFY(file.exists());
|
||||
|
||||
Profile::Ptr profile = Profile::Ptr(new Profile(builtin));
|
||||
|
||||
ProfileReader reader;
|
||||
QString parentProfilePath;
|
||||
QVERIFY(reader.readProfile(file.fileName(), profile, parentProfilePath));
|
||||
|
||||
QCOMPARE(profile->property<QString>(Profile::Icon), QStringLiteral("terminator"));
|
||||
// It's called "Built-in", but its parent is the real built-in profile
|
||||
QCOMPARE(profile->name(), builtin->name());
|
||||
QCOMPARE(parentProfilePath, builtin->path());
|
||||
}
|
||||
|
||||
void ProfileTest::testInvalidParentProfile()
|
||||
{
|
||||
auto *manager = ProfileManager::instance();
|
||||
|
||||
auto profileName = QFINDTESTDATA(QStringLiteral("data/invalid-parent.profile"));
|
||||
QFile file(profileName);
|
||||
QVERIFY(file.exists());
|
||||
|
||||
auto profile = manager->loadProfile(file.fileName());
|
||||
QVERIFY(profile);
|
||||
QCOMPARE(profile->property<QString>(Profile::Name), QStringLiteral("TestInvalidParentProfile"));
|
||||
|
||||
Profile::Ptr parent = profile->parent();
|
||||
QVERIFY(parent);
|
||||
QCOMPARE(parent->property<QString>(Profile::Name), QStringLiteral("Built-in"));
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(ProfileTest)
|
||||
|
||||
#include "moc_ProfileTest.cpp"
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef PROFILETEST_H
|
||||
#define PROFILETEST_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class ProfileTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void testProfile();
|
||||
void testClone();
|
||||
void testProfileGroup();
|
||||
void testProfileFileNames();
|
||||
void testProfileNameSorting();
|
||||
void testBuiltinProfile();
|
||||
void testLoadProfileNamedAsBuiltin();
|
||||
void testInvalidParentProfile();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // PROFILETEST_H
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "PtyTest.h"
|
||||
|
||||
// Qt
|
||||
#include <QSize>
|
||||
#include <QStringList>
|
||||
|
||||
// KDE
|
||||
#include <QTest>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
void PtyTest::init()
|
||||
{
|
||||
}
|
||||
|
||||
void PtyTest::cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
void PtyTest::testFlowControl()
|
||||
{
|
||||
Pty pty;
|
||||
const bool input = true;
|
||||
pty.setFlowControlEnabled(input);
|
||||
const bool output = pty.flowControlEnabled();
|
||||
QCOMPARE(output, input);
|
||||
}
|
||||
|
||||
void PtyTest::testEraseChar()
|
||||
{
|
||||
Pty pty;
|
||||
const char input = 'x';
|
||||
pty.setEraseChar(input);
|
||||
const char output = pty.eraseChar();
|
||||
QCOMPARE(output, input);
|
||||
}
|
||||
|
||||
void PtyTest::testUseUtmp()
|
||||
{
|
||||
Pty pty;
|
||||
const bool input = true;
|
||||
pty.setUseUtmp(input);
|
||||
const bool output = pty.isUseUtmp();
|
||||
QCOMPARE(output, input);
|
||||
}
|
||||
|
||||
void PtyTest::testWindowSize()
|
||||
{
|
||||
Pty pty;
|
||||
QSize input(80, 40);
|
||||
QSize pxInput(80 * 8, 40 * 16);
|
||||
pty.setWindowSize(input.width(), input.height(), pxInput.width(), pxInput.height());
|
||||
QSize output = pty.windowSize();
|
||||
QCOMPARE(output, input);
|
||||
QSize pxOutput = pty.pixelSize();
|
||||
QCOMPARE(pxOutput, pxInput);
|
||||
}
|
||||
|
||||
void PtyTest::testRunProgram()
|
||||
{
|
||||
Pty pty;
|
||||
QString program = QStringLiteral("sh");
|
||||
QStringList arguments;
|
||||
arguments << program;
|
||||
QStringList environments;
|
||||
const int result = pty.start(program, arguments, environments);
|
||||
|
||||
QCOMPARE(result, 0);
|
||||
auto fpg = pty.foregroundProcessGroup();
|
||||
auto pid = pty.processId();
|
||||
// FIXME: This often fails on FreeBSD CI
|
||||
// Actual (fpg): 100000; Expected (pid): 28534
|
||||
#if defined(Q_OS_FREEBSD)
|
||||
QSKIP("This often fails on CI FreeBSD CI", SkipSingle);
|
||||
#else
|
||||
QCOMPARE(fpg, pid);
|
||||
#endif
|
||||
pty.close();
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(PtyTest)
|
||||
|
||||
#include "moc_PtyTest.cpp"
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Robert Knight <robertknight@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef PTYTEST_H
|
||||
#define PTYTEST_H
|
||||
|
||||
#include "../Pty.h"
|
||||
|
||||
namespace Konsole
|
||||
{
|
||||
class PtyTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
void testFlowControl();
|
||||
void testEraseChar();
|
||||
void testUseUtmp();
|
||||
void testWindowSize();
|
||||
|
||||
void testRunProgram();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // PTYTEST_H
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Lukasz Kotula <lukasz.kotula@gmx.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "ScreenTest.h"
|
||||
|
||||
// Qt
|
||||
#include <QString>
|
||||
|
||||
// KDE
|
||||
#include <QTest>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
void ScreenTest::doLargeScreenCopyVerification(const QString &putToScreen, const QString &expectedSelection)
|
||||
{
|
||||
Screen screen(largeScreenLines, largeScreenColumns);
|
||||
|
||||
for (const auto &lineCharacter : putToScreen) {
|
||||
screen.displayCharacter(lineCharacter.toLatin1());
|
||||
}
|
||||
|
||||
screen.setSelectionStart(0, 0, false);
|
||||
screen.setSelectionEnd(largeScreenColumns, 0, false);
|
||||
QCOMPARE(screen.selectedText(Screen::PlainText), expectedSelection);
|
||||
}
|
||||
|
||||
void ScreenTest::testLargeScreenCopyShortLine()
|
||||
{
|
||||
const QString putToScreen = QStringLiteral("0123456789abcde");
|
||||
const QString expectedSelection = QStringLiteral("0123456789abcde\n");
|
||||
doLargeScreenCopyVerification(putToScreen, expectedSelection);
|
||||
}
|
||||
|
||||
void ScreenTest::testBlockSelection()
|
||||
{
|
||||
Screen screen(largeScreenLines, largeScreenColumns);
|
||||
|
||||
const QString reallyBigTextForReflow = QStringLiteral("abcd efgh ijkl mnop qrst uvxz ABCD EFGH IJKL MNOP QRST UVXZ");
|
||||
|
||||
for (const QChar &c : reallyBigTextForReflow) {
|
||||
screen.displayCharacter(c.toLatin1());
|
||||
}
|
||||
|
||||
// this breaks the lines in `abcd efgh `
|
||||
// reflowing everything to the lines below.
|
||||
screen.setReflowLines(true);
|
||||
|
||||
// reflow does not reflows cursor line, so let's move it a bit down.
|
||||
screen.cursorDown(1);
|
||||
screen.resizeImage(largeScreenLines, 10);
|
||||
|
||||
// True here means block selection.
|
||||
screen.setSelectionStart(0, 0, true);
|
||||
screen.setSelectionEnd(3, 1, false);
|
||||
|
||||
// after the resize, the string should be:
|
||||
// abcd efgh
|
||||
// ijkl mnop
|
||||
// ...
|
||||
// I'm selecting the first two lines of the first column of strings,
|
||||
// so, abcd ijkl.
|
||||
const QString selectedText = screen.selectedText(Screen::PlainText);
|
||||
QCOMPARE(screen.selectedText(Screen::PlainText), QStringLiteral("abcd ijkl"));
|
||||
}
|
||||
|
||||
void ScreenTest::testCJKBlockSelection()
|
||||
{
|
||||
Screen screen(largeScreenLines, largeScreenColumns);
|
||||
|
||||
const QString reallyBigTextForReflow = QStringLiteral(
|
||||
// Precomposed Hangul (NFC, each syllable block is a codepoint)
|
||||
"챠트 피면 술컵"
|
||||
"01234567890123"
|
||||
" 도 유효작 "
|
||||
"01234567890123"
|
||||
// Decomposed Hangul (NFD, syllables are made of several jamos)
|
||||
"챠트 피면 술컵"
|
||||
"01234567890123"
|
||||
" 도 유효작 "
|
||||
// Iroha (a pangrammic Japanese poem)
|
||||
"いろはにほへと"
|
||||
"01234567890123"
|
||||
" ちりぬるを "
|
||||
"01234567890123"
|
||||
"わかよたれそ "
|
||||
"01234567890123"
|
||||
" つねならむ "
|
||||
"01234567890123"
|
||||
"うゐのおくやま"
|
||||
"01234567890123"
|
||||
" けふこえて "
|
||||
"01234567890123"
|
||||
"あさきゆめみし"
|
||||
"01234567890123"
|
||||
"ゑひもせす");
|
||||
|
||||
for (const QChar &c : reallyBigTextForReflow) {
|
||||
screen.displayCharacter(c.unicode());
|
||||
}
|
||||
|
||||
// this breaks the text so it looks like above
|
||||
screen.setReflowLines(true);
|
||||
|
||||
// reflow does not reflows cursor line, so let's move it a bit down.
|
||||
screen.cursorDown(1);
|
||||
screen.resizeImage(32, 14);
|
||||
|
||||
// True here means block selection.
|
||||
screen.setSelectionStart(2, 0, true);
|
||||
screen.setSelectionEnd(6, 15, false);
|
||||
|
||||
// Do a block selection and compare the result to a known good result
|
||||
QCOMPARE(screen.selectedText(Screen::PlainText),
|
||||
QStringLiteral("\uD2B8 \uD53C 23456 \uC720\uD6A8 23456 \u1110\u1173 \u1111\u1175 23456 \u110B\u1172\u1112\u116D \u308D\u306F\u306B 23456 "
|
||||
"\u308A\u306C 23456 \u304B\u3088\u305F 23456 \u306D\u306A 23456 \u3090\u306E\u304A"));
|
||||
}
|
||||
|
||||
void ScreenTest::testLargeScreenCopyEmptyLine()
|
||||
{
|
||||
const QString putToScreen;
|
||||
const QString expectedSelection = QStringLiteral("\n");
|
||||
|
||||
doLargeScreenCopyVerification(putToScreen, expectedSelection);
|
||||
}
|
||||
|
||||
void ScreenTest::testLargeScreenCopyLongLine()
|
||||
{
|
||||
QString putToScreen;
|
||||
|
||||
// Make the line longer than screen size (1300 characters)
|
||||
for (int i = 0; i < 130; ++i) {
|
||||
putToScreen.append(QStringLiteral("0123456789"));
|
||||
}
|
||||
const QString expectedSelection = putToScreen.left(1200);
|
||||
|
||||
doLargeScreenCopyVerification(putToScreen, expectedSelection);
|
||||
}
|
||||
|
||||
void ScreenTest::doComparePosition(Screen *screen, int y, int x)
|
||||
{
|
||||
QCOMPARE(screen->getCursorY(), y);
|
||||
QCOMPARE(screen->getCursorX(), x);
|
||||
}
|
||||
|
||||
// Test: setCursorYX, setCursorX, setCursorY, cursorDown, cursorUp,
|
||||
// cursorRight, cursorLeft, cursorNextLine and cursorPreviousLine
|
||||
void ScreenTest::testCursorPosition()
|
||||
{
|
||||
Screen *screen = new Screen(largeScreenLines, largeScreenColumns);
|
||||
|
||||
// setCursorYX will test setCursorX and setCursorY too
|
||||
screen->setCursorYX(6, 6);
|
||||
doComparePosition(screen, 5, 5);
|
||||
|
||||
screen->setCursorYX(2147483647, 2147483647);
|
||||
doComparePosition(screen, largeScreenLines - 1, largeScreenColumns - 1);
|
||||
|
||||
screen->setCursorYX(-1, -1);
|
||||
doComparePosition(screen, 0, 0);
|
||||
|
||||
screen->setCursorYX(0, 0);
|
||||
doComparePosition(screen, 0, 0);
|
||||
|
||||
screen->setCursorYX(1, 1);
|
||||
doComparePosition(screen, 0, 0);
|
||||
|
||||
screen->cursorDown(2147483647);
|
||||
doComparePosition(screen, largeScreenLines - 1, 0);
|
||||
|
||||
screen->cursorUp(2147483647);
|
||||
doComparePosition(screen, 0, 0);
|
||||
|
||||
screen->cursorDown(4);
|
||||
doComparePosition(screen, 4, 0);
|
||||
|
||||
screen->cursorDown(-1);
|
||||
doComparePosition(screen, 5, 0);
|
||||
|
||||
screen->cursorDown(0);
|
||||
doComparePosition(screen, 6, 0);
|
||||
|
||||
screen->cursorUp(0);
|
||||
doComparePosition(screen, 5, 0);
|
||||
|
||||
screen->cursorUp(-1);
|
||||
doComparePosition(screen, 4, 0);
|
||||
|
||||
screen->cursorUp(4);
|
||||
doComparePosition(screen, 0, 0);
|
||||
|
||||
screen->cursorRight(-1);
|
||||
doComparePosition(screen, 0, 1);
|
||||
|
||||
screen->cursorRight(3);
|
||||
doComparePosition(screen, 0, 4);
|
||||
|
||||
screen->cursorRight(0);
|
||||
doComparePosition(screen, 0, 5);
|
||||
|
||||
screen->cursorLeft(0);
|
||||
doComparePosition(screen, 0, 4);
|
||||
|
||||
screen->cursorLeft(2);
|
||||
doComparePosition(screen, 0, 2);
|
||||
|
||||
screen->cursorLeft(-1);
|
||||
doComparePosition(screen, 0, 1);
|
||||
|
||||
screen->cursorRight(2147483647);
|
||||
doComparePosition(screen, 0, largeScreenColumns - 1);
|
||||
|
||||
screen->cursorLeft(2147483647);
|
||||
doComparePosition(screen, 0, 0);
|
||||
|
||||
screen->cursorNextLine(4);
|
||||
doComparePosition(screen, 4, 0);
|
||||
|
||||
screen->cursorNextLine(-1);
|
||||
doComparePosition(screen, 5, 0);
|
||||
|
||||
screen->cursorNextLine(0);
|
||||
doComparePosition(screen, 6, 0);
|
||||
|
||||
screen->cursorPreviousLine(0);
|
||||
doComparePosition(screen, 5, 0);
|
||||
|
||||
screen->cursorPreviousLine(2);
|
||||
doComparePosition(screen, 3, 0);
|
||||
|
||||
screen->cursorPreviousLine(-1);
|
||||
doComparePosition(screen, 2, 0);
|
||||
|
||||
screen->cursorPreviousLine(2147483647);
|
||||
doComparePosition(screen, 0, 0);
|
||||
|
||||
screen->cursorNextLine(2147483647);
|
||||
doComparePosition(screen, largeScreenLines - 1, 0);
|
||||
|
||||
delete screen;
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(ScreenTest)
|
||||
|
||||
#include "moc_ScreenTest.cpp"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user