Commit 8e8b9243 authored by Jonah Brüchert's avatar Jonah Brüchert 🌳 Committed by Tomaz Canabrava
Browse files

TerminalDisplay: Move out common pasting related code

parent cd207665
......@@ -184,6 +184,7 @@ set(konsoleprivate_SRCS ${windowadaptors_SRCS}
terminalDisplay/TerminalScrollBar.cpp
terminalDisplay/TerminalColor.cpp
terminalDisplay/TerminalFonts.cpp
terminalDisplay/TerminalPasting.cpp
widgets/TerminalDisplayAccessible.cpp
widgets/TerminalHeaderBar.cpp
......
......@@ -1078,7 +1078,7 @@ void SessionController::copy()
void SessionController::paste()
{
view()->pasteFromClipboard();
Q_EMIT pasteFromClipboardRequested();
}
void SessionController::pasteFromX11Selection()
{
......
......@@ -172,6 +172,11 @@ Q_SIGNALS:
*/
void requestPrint();
/**
* indicates to the connected terminal display that it should paste from the clipboard
*/
void pasteFromClipboardRequested();
public Q_SLOTS:
/**
* Issues a command to the session to navigate to the specified URL.
......
......@@ -75,6 +75,7 @@
#include "TerminalScrollBar.h"
#include "TerminalColor.h"
#include "TerminalFonts.h"
#include "TerminalPasting.h"
using namespace Konsole;
......@@ -1564,7 +1565,7 @@ void TerminalDisplay::processMidButtonClick(QMouseEvent* ev)
if (_middleClickPasteMode == Enum::PasteFromX11Selection) {
pasteFromX11Selection(appendEnter);
} else if (_middleClickPasteMode == Enum::PasteFromClipboard) {
pasteFromClipboard(appendEnter);
doPaste(terminalPasting::pasteFromClipboard(), appendEnter);
} else {
Q_ASSERT(false);
}
......@@ -2103,10 +2104,6 @@ void TerminalDisplay::doPaste(QString text, bool appendReturn)
return;
}
if (appendReturn) {
text.append(QLatin1String("\r"));
}
if (text.length() > 8000) {
if (KMessageBox::warningContinueCancel(window(),
i18np("Are you sure you want to paste %1 character?",
......@@ -2120,56 +2117,7 @@ void TerminalDisplay::doPaste(QString text, bool appendReturn)
}
}
// Most code in Konsole uses UTF-32. We're filtering
// UTF-16 here, as all control characters can be represented
// in this encoding as single code unit. If you ever need to
// filter anything above 0xFFFF (specific code points or
// categories which contain such code points), convert text to
// UTF-32 using QString::toUcs4() and use QChar static
// methods which take "uint ucs4".
static const QVector<ushort> whitelist = { u'\t', u'\r', u'\n' };
static const auto isUnsafe = [](const QChar &c) {
return (c.category() == QChar::Category::Other_Control && !whitelist.contains(c.unicode()));
};
// Returns control sequence string (e.g. "^C") for control character c
static const auto charToSequence = [](const QChar &c) {
if (c.unicode() <= 0x1F) {
return QStringLiteral("^%1").arg(QChar(u'@' + c.unicode()));
} else if (c.unicode() == 0x7F) {
return QStringLiteral("^?");
} else if (c.unicode() >= 0x80 && c.unicode() <= 0x9F){
return QStringLiteral("^[%1").arg(QChar(u'@' + c.unicode() - 0x80));
}
return QString();
};
const QMap<ushort, QString> characterDescriptions = {
{0x0003, i18n("End Of Text/Interrupt: may exit the current process")},
{0x0004, i18n("End Of Transmission: may exit the current process")},
{0x0007, i18n("Bell: will try to emit an audible warning")},
{0x0008, i18n("Backspace")},
{0x0013, i18n("Device Control Three/XOFF: suspends output")},
{0x001a, i18n("Substitute/Suspend: may suspend current process")},
{0x001b, i18n("Escape: used for manipulating terminal state")},
{0x001c, i18n("File Separator/Quit: may abort the current process")},
};
QStringList unsafeCharacters;
for (const QChar &c : text) {
if (isUnsafe(c)) {
const QString sequence = charToSequence(c);
const QString description = characterDescriptions.value(c.unicode(), QString());
QString entry = QStringLiteral("U+%1").arg(c.unicode(), 4, 16, QLatin1Char('0'));
if(!sequence.isEmpty()) {
entry += QStringLiteral("\t%1").arg(sequence);
}
if(!description.isEmpty()) {
entry += QStringLiteral("\t%1").arg(description);
}
unsafeCharacters.append(entry);
}
}
unsafeCharacters.removeDuplicates();
auto unsafeCharacters = terminalPasting::checkForUnsafeCharacters(text);
if (!unsafeCharacters.isEmpty()) {
int result = KMessageBox::warningYesNoCancelList(window(),
......@@ -2192,13 +2140,7 @@ void TerminalDisplay::doPaste(QString text, bool appendReturn)
case KMessageBox::Cancel:
return;
case KMessageBox::Yes: {
QString sanitized;
for (const QChar &c : text) {
if (!isUnsafe(c)) {
sanitized.append(c);
}
}
text = sanitized;
text = terminalPasting::sanitizeString(text);
}
case KMessageBox::No:
break;
......@@ -2207,17 +2149,8 @@ void TerminalDisplay::doPaste(QString text, bool appendReturn)
}
}
if (!text.isEmpty()) {
// replace CRLF with CR first, fixes issues with pasting multiline
// text from gtk apps (e.g. Firefox), bug 421480
text.replace(QLatin1String("\r\n"), QLatin1String("\r"));
text.replace(QLatin1Char('\n'), QLatin1Char('\r'));
if (bracketedPasteMode()) {
text.remove(QLatin1String("\033"));
text.prepend(QLatin1String("\033[200~"));
text.append(QLatin1String("\033[201~"));
}
auto pasteString = terminalPasting::prepareStringForPasting(text, appendReturn, bracketedPasteMode());
if (pasteString.has_value()) {
// perform paste by simulating keypress events
QKeyEvent e(QEvent::KeyPress, 0, Qt::NoModifier, text);
Q_EMIT keyPressedSignal(&e);
......@@ -2288,32 +2221,6 @@ void TerminalDisplay::copyToClipboard()
QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard);
}
void TerminalDisplay::pasteFromClipboard(bool appendEnter)
{
QString text;
const QMimeData *mimeData = QApplication::clipboard()->mimeData(QClipboard::Clipboard);
// When pasting urls of local files:
// - remove the scheme part, "file://"
// - paste the path(s) as a space-separated list of strings, which are quoted if needed
if (!mimeData->hasUrls()) { // fast path if there are no urls
text = mimeData->text();
} else { // handle local file urls
const QList<QUrl> list = mimeData->urls();
for (const QUrl &url : list) {
if (url.isLocalFile()) {
text += KShell::quoteArg(url.toLocalFile());
text += QLatin1Char(' ');
} else { // can users copy urls of both local and remote files at the same time?
text = mimeData->text();
break;
}
}
}
doPaste(text, appendEnter);
}
void TerminalDisplay::pasteFromX11Selection(bool appendEnter)
{
if (QApplication::clipboard()->supportsSelection()) {
......@@ -2793,6 +2700,9 @@ void TerminalDisplay::doDrag()
void TerminalDisplay::setSessionController(SessionController* controller)
{
_sessionController = controller;
connect(_sessionController, &Konsole::SessionController::pasteFromClipboardRequested, [this] {
doPaste(terminalPasting::pasteFromClipboard(), false);
});
_headerBar->finishHeaderSetup(controller);
}
......
......@@ -368,14 +368,6 @@ public Q_SLOTS:
/** Copies the selected text to the system clipboard. */
void copyToClipboard();
/**
* Pastes the content of the clipboard into the display.
*
* URLs of local files are treated specially:
* - The scheme part, "file://", is removed from each URL
* - The URLs are pasted as a space-separated list of file paths
*/
void pasteFromClipboard(bool appendEnter = false);
/**
* Pastes the content of the X11 selection into the
* display.
......
/*
SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
SPDX-FileCopyrightText: 2021 Jonah Brüchert <jbb@kaidan.im>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QMimeData>
#include <QString>
#include <QApplication>
#include <QClipboard>
#include <QUrl>
#include <QKeyEvent>
#include <KShell>
#include <KLocalizedString>
#include "TerminalPasting.h"
namespace Konsole {
namespace terminalPasting {
// Most code in Konsole uses UTF-32. We're filtering
// UTF-16 here, as all control characters can be represented
// in this encoding as single code unit. If you ever need to
// filter anything above 0xFFFF (specific code points or
// categories which contain such code points), convert text to
// UTF-32 using QString::toUcs4() and use QChar static
// methods which take "uint ucs4".
static constexpr std::array<ushort, 3> ALLOWLIST = { u'\t', u'\r', u'\n' };
QString pasteFromClipboard()
{
QString text;
const QMimeData *mimeData = QApplication::clipboard()->mimeData(QClipboard::Clipboard);
// When pasting urls of local files:
// - remove the scheme part, "file://"
// - paste the path(s) as a space-separated list of strings, which are quoted if needed
if (!mimeData->hasUrls()) { // fast path if there are no urls
text = mimeData->text();
} else { // handle local file urls
const QList<QUrl> list = mimeData->urls();
for (const QUrl &url : list) {
if (url.isLocalFile()) {
text += KShell::quoteArg(url.toLocalFile());
text += QLatin1Char(' ');
} else { // can users copy urls of both local and remote files at the same time?
text = mimeData->text();
break;
}
}
}
return text;
}
QString sanitizeString(const QString &text)
{
QString sanitized;
for (const QChar &c : text) {
if (!isUnsafe(c)) {
sanitized.append(c);
}
}
return sanitized;
}
std::optional<QString> prepareStringForPasting(QString text, bool appendReturn, bool bracketedPasteMode)
{
if (appendReturn) {
text.append(QLatin1String("\r"));
}
if (!text.isEmpty()) {
// replace CRLF with CR first, fixes issues with pasting multiline
// text from gtk apps (e.g. Firefox), bug 421480
text.replace(QLatin1String("\r\n"), QLatin1String("\r"));
text.replace(QLatin1Char('\n'), QLatin1Char('\r'));
if (bracketedPasteMode) {
text.remove(QLatin1String("\033"));
text.prepend(QLatin1String("\033[200~"));
text.append(QLatin1String("\033[201~"));
}
return text;
}
return {};
}
QStringList checkForUnsafeCharacters(const QString &text)
{
// Returns control sequence string (e.g. "^C") for control character c
static const auto charToSequence = [](const QChar &c) {
if (c.unicode() <= 0x1F) {
return QStringLiteral("^%1").arg(QChar(u'@' + c.unicode()));
} else if (c.unicode() == 0x7F) {
return QStringLiteral("^?");
} else if (c.unicode() >= 0x80 && c.unicode() <= 0x9F){
return QStringLiteral("^[%1").arg(QChar(u'@' + c.unicode() - 0x80));
}
return QString();
};
const QMap<ushort, QString> characterDescriptions = {
{0x0003, i18n("End Of Text/Interrupt: may exit the current process")},
{0x0004, i18n("End Of Transmission: may exit the current process")},
{0x0007, i18n("Bell: will try to emit an audible warning")},
{0x0008, i18n("Backspace")},
{0x0013, i18n("Device Control Three/XOFF: suspends output")},
{0x001a, i18n("Substitute/Suspend: may suspend current process")},
{0x001b, i18n("Escape: used for manipulating terminal state")},
{0x001c, i18n("File Separator/Quit: may abort the current process")},
};
QStringList unsafeCharactersDescriptions;
for (const QChar &c : text) {
if (isUnsafe(c)) {
const QString sequence = charToSequence(c);
const QString description = characterDescriptions.value(c.unicode(), QString());
QString entry = QStringLiteral("U+%1").arg(c.unicode(), 4, 16, QLatin1Char('0'));
if(!sequence.isEmpty()) {
entry += QStringLiteral("\t%1").arg(sequence);
}
if(!description.isEmpty()) {
entry += QStringLiteral("\t%1").arg(description);
}
unsafeCharactersDescriptions.append(entry);
}
}
unsafeCharactersDescriptions.removeDuplicates();
return unsafeCharactersDescriptions;
}
bool isUnsafe(const QChar c) {
return (c.category() == QChar::Category::Other_Control && std::find(ALLOWLIST.begin(), ALLOWLIST.end(), c.unicode()) != ALLOWLIST.end());
}
}
}
/*
SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
SPDX-FileCopyrightText: 2021 Jonah Brüchert <jbb@kaidan.im>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
class QString;
class QStringList;
#include <QChar>
#include <optional>
namespace Konsole {
namespace terminalPasting {
/**
* Retrieves the content of the clipboard and preprocesses it for pasting
* into the display.
*
* URLs of local files are treated specially:
* - The scheme part, "file://", is removed from each URL
* - The URLs are pasted as a space-separated list of file paths
*/
QString pasteFromClipboard();
/**
* Removes unsafe characters from the string
*/
QString sanitizeString(const QString &text);
/**
* Does various string operations in preparation for pasting the string into a terminal display
*/
std::optional<QString> prepareStringForPasting(QString text, bool appendReturn, bool bracketedPasteMode);
/**
* Creates a list of localized description of unsafe characters contained in the given string.
*/
QStringList checkForUnsafeCharacters(const QString &text);
/**
* Check if it's unsafe to paste the given character into a terminal
*/
bool isUnsafe(const QChar c);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment