You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
365 lines
13 KiB
365 lines
13 KiB
/* |
|
This source file is part of Konsole, a terminal emulator. |
|
|
|
SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com> |
|
SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> |
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
|
|
// Own |
|
#include "Profile.h" |
|
|
|
// Qt |
|
#include <QRegularExpression> |
|
#include <QTextCodec> |
|
|
|
// KDE |
|
#include <KLocalizedString> |
|
#include <QFontDatabase> |
|
#include <kcoreaddons_version.h> |
|
|
|
// Konsole |
|
#include "Enumeration.h" |
|
#include "ProfileGroup.h" |
|
#include "config-konsole.h" |
|
|
|
#ifdef HAVE_GETPWUID |
|
#include <pwd.h> |
|
#include <sys/types.h> |
|
#include <unistd.h> |
|
#endif |
|
|
|
#if KCOREADDONS_VERSION >= QT_VERSION_CHECK(5, 97, 0) |
|
#include <KSandbox> |
|
#endif |
|
|
|
using namespace Konsole; |
|
|
|
// mappings between property enum values and names |
|
// |
|
// multiple names are defined for some property values, |
|
// in these cases, the "proper" string name comes first, |
|
// as that is used when reading/writing profiles from/to disk |
|
// |
|
// the other names are usually shorter versions for convenience |
|
// when parsing konsoleprofile commands |
|
static const char GENERAL_GROUP[] = "General"; |
|
static const char KEYBOARD_GROUP[] = "Keyboard"; |
|
static const char APPEARANCE_GROUP[] = "Appearance"; |
|
static const char SCROLLING_GROUP[] = "Scrolling"; |
|
static const char TERMINAL_GROUP[] = "Terminal Features"; |
|
static const char CURSOR_GROUP[] = "Cursor Options"; |
|
static const char INTERACTION_GROUP[] = "Interaction Options"; |
|
static const char ENCODING_GROUP[] = "Encoding Options"; |
|
|
|
const std::vector<Profile::PropertyInfo> Profile::DefaultProperties = { |
|
// General |
|
{Path, "Path", nullptr, QString()}, |
|
{Name, "Name", GENERAL_GROUP, QString()}, |
|
{UntranslatedName, "UntranslatedName", nullptr, QString()}, |
|
{Icon, "Icon", GENERAL_GROUP, QLatin1String("utilities-terminal")}, |
|
{Command, "Command", nullptr, QString()}, |
|
{Arguments, "Arguments", nullptr, QStringList()}, |
|
{MenuIndex, "MenuIndex", nullptr, QLatin1String("0")}, |
|
{Environment, "Environment", GENERAL_GROUP, QStringList{QLatin1String("TERM=xterm-256color"), QLatin1String("COLORTERM=truecolor")}}, |
|
{Directory, "Directory", GENERAL_GROUP, QString()}, |
|
{LocalTabTitleFormat, "LocalTabTitleFormat", GENERAL_GROUP, QLatin1String("%d : %n")}, |
|
{LocalTabTitleFormat, "tabtitle", nullptr, QLatin1String("%d : %n")}, |
|
{RemoteTabTitleFormat, "RemoteTabTitleFormat", GENERAL_GROUP, QLatin1String("(%u) %H")}, |
|
{SemanticHints, "SemanticHints", GENERAL_GROUP, 1}, |
|
{SemanticUpDown, "SemanticUpDown", GENERAL_GROUP, false}, |
|
{SemanticInputClick, "SemanticInputClick", GENERAL_GROUP, false}, |
|
{ShowTerminalSizeHint, "ShowTerminalSizeHint", GENERAL_GROUP, true}, |
|
{StartInCurrentSessionDir, "StartInCurrentSessionDir", GENERAL_GROUP, true}, |
|
{SilenceSeconds, "SilenceSeconds", GENERAL_GROUP, 10}, |
|
{TerminalColumns, "TerminalColumns", GENERAL_GROUP, 110}, |
|
{TerminalRows, "TerminalRows", GENERAL_GROUP, 28}, |
|
{TerminalMargin, "TerminalMargin", GENERAL_GROUP, 1}, |
|
{TerminalCenter, "TerminalCenter", GENERAL_GROUP, false}, |
|
|
|
// Appearance |
|
{Font, "Font", APPEARANCE_GROUP, QFont()}, |
|
{ColorScheme, "ColorScheme", APPEARANCE_GROUP, QLatin1String("Breeze")}, |
|
{ColorScheme, "colors", nullptr, QLatin1String("Breeze")}, |
|
{AntiAliasFonts, "AntiAliasFonts", APPEARANCE_GROUP, true}, |
|
{BoldIntense, "BoldIntense", APPEARANCE_GROUP, true}, |
|
{UseFontLineCharacters, "UseFontLineChararacters", APPEARANCE_GROUP, false}, |
|
{LineSpacing, "LineSpacing", APPEARANCE_GROUP, 0}, |
|
{TabColor, "TabColor", APPEARANCE_GROUP, QColor(QColor::Invalid)}, |
|
{DimValue, "DimmValue", APPEARANCE_GROUP, 128}, |
|
{DimWhenInactive, "DimWhenInactive", GENERAL_GROUP, false}, |
|
{InvertSelectionColors, "InvertSelectionColors", GENERAL_GROUP, false}, |
|
|
|
// Keyboard |
|
#ifdef Q_OS_MACOS |
|
{KeyBindings, "KeyBindings", KEYBOARD_GROUP, QLatin1String("macos")}, |
|
#else |
|
{KeyBindings, "KeyBindings", KEYBOARD_GROUP, QLatin1String("default")}, |
|
#endif |
|
|
|
// Scrolling |
|
{HistoryMode, "HistoryMode", SCROLLING_GROUP, Enum::FixedSizeHistory}, |
|
{HistorySize, "HistorySize", SCROLLING_GROUP, 1000}, |
|
{ScrollBarPosition, "ScrollBarPosition", SCROLLING_GROUP, Enum::ScrollBarRight}, |
|
{ScrollFullPage, "ScrollFullPage", SCROLLING_GROUP, false}, |
|
{HighlightScrolledLines, "HighlightScrolledLines", SCROLLING_GROUP, true}, |
|
{ReflowLines, "ReflowLines", SCROLLING_GROUP, true}, |
|
|
|
// Terminal Features |
|
{UrlHintsModifiers, "UrlHintsModifiers", TERMINAL_GROUP, 0}, |
|
{ReverseUrlHints, "ReverseUrlHints", TERMINAL_GROUP, false}, |
|
{BlinkingTextEnabled, "BlinkingTextEnabled", TERMINAL_GROUP, true}, |
|
{FlowControlEnabled, "FlowControlEnabled", TERMINAL_GROUP, true}, |
|
{BidiRenderingEnabled, "BidiRenderingEnabled", TERMINAL_GROUP, true}, |
|
{BlinkingCursorEnabled, "BlinkingCursorEnabled", TERMINAL_GROUP, false}, |
|
{BellMode, "BellMode", TERMINAL_GROUP, Enum::NotifyBell}, |
|
{VerticalLine, "VerticalLine", TERMINAL_GROUP, false}, |
|
{VerticalLineAtChar, "VerticalLineAtChar", TERMINAL_GROUP, 80}, |
|
{PeekPrimaryKeySequence, "PeekPrimaryKeySequence", TERMINAL_GROUP, QString()}, |
|
|
|
// Cursor |
|
{UseCustomCursorColor, "UseCustomCursorColor", CURSOR_GROUP, false}, |
|
{CursorShape, "CursorShape", CURSOR_GROUP, Enum::BlockCursor}, |
|
{CustomCursorColor, "CustomCursorColor", CURSOR_GROUP, QColor(Qt::white)}, |
|
{CustomCursorTextColor, "CustomCursorTextColor", CURSOR_GROUP, QColor(Qt::black)}, |
|
|
|
// Interaction |
|
{WordCharacters, "WordCharacters", INTERACTION_GROUP, QLatin1String(":@-./_~?&=%+#")}, |
|
{TripleClickMode, "TripleClickMode", INTERACTION_GROUP, Enum::SelectWholeLine}, |
|
{UnderlineLinksEnabled, "UnderlineLinksEnabled", INTERACTION_GROUP, true}, |
|
{UnderlineFilesEnabled, "UnderlineFilesEnabled", INTERACTION_GROUP, false}, |
|
{OpenLinksByDirectClickEnabled, "OpenLinksByDirectClickEnabled", INTERACTION_GROUP, false}, |
|
{TextEditorCmd, "TextEditorCmd", INTERACTION_GROUP, Enum::Kate}, |
|
{TextEditorCmdCustom, "TextEditorCmdCustom", INTERACTION_GROUP, QLatin1String("kate PATH:LINE:COLUMN")}, |
|
{CtrlRequiredForDrag, "CtrlRequiredForDrag", INTERACTION_GROUP, true}, |
|
{DropUrlsAsText, "DropUrlsAsText", INTERACTION_GROUP, true}, |
|
{AutoCopySelectedText, "AutoCopySelectedText", INTERACTION_GROUP, false}, |
|
{CopyTextAsHTML, "CopyTextAsHTML", INTERACTION_GROUP, true}, |
|
{TrimLeadingSpacesInSelectedText, "TrimLeadingSpacesInSelectedText", INTERACTION_GROUP, false}, |
|
{TrimTrailingSpacesInSelectedText, "TrimTrailingSpacesInSelectedText", INTERACTION_GROUP, false}, |
|
{PasteFromSelectionEnabled, "PasteFromSelectionEnabled", INTERACTION_GROUP, true}, |
|
{PasteFromClipboardEnabled, "PasteFromClipboardEnabled", INTERACTION_GROUP, false}, |
|
{MiddleClickPasteMode, "MiddleClickPasteMode", INTERACTION_GROUP, Enum::PasteFromX11Selection}, |
|
{MouseWheelZoomEnabled, "MouseWheelZoomEnabled", INTERACTION_GROUP, true}, |
|
{AllowMouseTracking, "AllowMouseTracking", INTERACTION_GROUP, true}, |
|
{AlternateScrolling, "AlternateScrolling", INTERACTION_GROUP, true}, |
|
{AllowEscapedLinks, "AllowEscapedLinks", INTERACTION_GROUP, false}, |
|
{EscapedLinksSchema, "EscapedLinksSchema", INTERACTION_GROUP, QLatin1String("http://;https://;file://")}, |
|
{ColorFilterEnabled, "ColorFilterEnabled", INTERACTION_GROUP, true}, |
|
|
|
// Encoding |
|
{DefaultEncoding, "DefaultEncoding", ENCODING_GROUP, QString()}, |
|
}; |
|
|
|
QHash<QString, Profile::PropertyInfo> Profile::PropertyInfoByName; |
|
|
|
// Magic path for the built-in profile which is not a valid file name, |
|
// thus it can not interfere with regular profiles. |
|
// For backward compatibility with existing profiles, it should never change. |
|
static const QString BUILTIN_MAGIC_PATH = QStringLiteral("FALLBACK/"); |
|
|
|
// UntranslatedName property of the built-in profile, as a char array. |
|
// |
|
// Note: regular profiles created in earlier versions of Konsole may have this name too. |
|
static const char BUILTIN_UNTRANSLATED_NAME_CHAR[] = "Built-in"; |
|
|
|
static QString defaultShell() |
|
{ |
|
#if KCOREADDONS_VERSION >= QT_VERSION_CHECK(5, 97, 0) |
|
if (!KSandbox::isFlatpak()) { |
|
return QString::fromUtf8(qgetenv("SHELL")); |
|
} |
|
|
|
#ifdef HAVE_GETPWUID |
|
auto pw = getpwuid(getuid()); |
|
// pw: Do not pass the returned pointer to free. |
|
if (pw != nullptr) { |
|
QProcess proc; |
|
proc.setProgram(QStringLiteral("getent")); |
|
proc.setArguments({QStringLiteral("passwd"), QString::number(pw->pw_uid)}); |
|
KSandbox::startHostProcess(proc); |
|
proc.waitForFinished(); |
|
return QString::fromUtf8(proc.readAllStandardOutput().simplified().split(':').at(6)); |
|
} |
|
return {}; |
|
#endif |
|
#else |
|
return QString::fromUtf8(qgetenv("SHELL")); |
|
#endif |
|
} |
|
|
|
void Profile::fillTableWithDefaultNames() |
|
{ |
|
static bool filledDefaults = false; |
|
|
|
if (filledDefaults) { |
|
return; |
|
} |
|
|
|
std::for_each(DefaultProperties.cbegin(), DefaultProperties.cend(), Profile::registerProperty); |
|
|
|
filledDefaults = true; |
|
} |
|
|
|
void Profile::useBuiltin() |
|
{ |
|
for (const PropertyInfo &propInfo : DefaultProperties) { |
|
setProperty(propInfo.property, propInfo.defaultValue); |
|
} |
|
setProperty(Name, i18nc("Name of the built-in profile", BUILTIN_UNTRANSLATED_NAME_CHAR)); |
|
setProperty(UntranslatedName, QString::fromLatin1(BUILTIN_UNTRANSLATED_NAME_CHAR)); |
|
setProperty(Path, BUILTIN_MAGIC_PATH); |
|
setProperty(Command, defaultShell()); |
|
// See Pty.cpp on why Arguments is populated |
|
setProperty(Arguments, QStringList{defaultShell()}); |
|
setProperty(Font, QFontDatabase::systemFont(QFontDatabase::FixedFont)); |
|
setProperty(DefaultEncoding, QLatin1String(QTextCodec::codecForLocale()->name())); |
|
// Built-in profile should not be shown in menus |
|
setHidden(true); |
|
} |
|
|
|
Profile::Profile(const Profile::Ptr &parent) |
|
: _propertyValues(PropertyMap()) |
|
, _parent(parent) |
|
, _hidden(false) |
|
{ |
|
} |
|
|
|
void Profile::clone(Profile::Ptr profile, bool differentOnly) |
|
{ |
|
for (const PropertyInfo &info : DefaultProperties) { |
|
Property current = info.property; |
|
QVariant otherValue = profile->property<QVariant>(current); |
|
if (current == Name || current == Path) { // These are unique per Profile |
|
continue; |
|
} |
|
if (!differentOnly || property<QVariant>(current) != otherValue) { |
|
setProperty(current, otherValue); |
|
} |
|
} |
|
} |
|
|
|
Profile::~Profile() = default; |
|
|
|
bool Profile::isBuiltin() const |
|
{ |
|
return path() == BUILTIN_MAGIC_PATH; |
|
} |
|
|
|
bool Profile::isHidden() const |
|
{ |
|
return _hidden; |
|
} |
|
void Profile::setHidden(bool hidden) |
|
{ |
|
_hidden = hidden; |
|
} |
|
|
|
void Profile::setParent(const Profile::Ptr &parent) |
|
{ |
|
_parent = parent; |
|
} |
|
const Profile::Ptr Profile::parent() const |
|
{ |
|
return _parent; |
|
} |
|
|
|
bool Profile::isEmpty() const |
|
{ |
|
return _propertyValues.isEmpty(); |
|
} |
|
|
|
Profile::PropertyMap Profile::properties() const |
|
{ |
|
return _propertyValues; |
|
} |
|
|
|
void Profile::setProperty(Property p, const QVariant &value) |
|
{ |
|
_propertyValues.insert(p, value); |
|
} |
|
|
|
void Profile::assignProperties(const PropertyMap &map) |
|
{ |
|
_propertyValues.insert(map); |
|
} |
|
|
|
bool Profile::isPropertySet(Property p) const |
|
{ |
|
return _propertyValues.contains(p); |
|
} |
|
|
|
Profile::Property Profile::lookupByName(const QString &name) |
|
{ |
|
// insert default names into table the first time this is called |
|
fillTableWithDefaultNames(); |
|
|
|
return PropertyInfoByName[name.toLower()].property; |
|
} |
|
|
|
void Profile::registerProperty(const PropertyInfo &info) |
|
{ |
|
QString name = QLatin1String(info.name); |
|
PropertyInfoByName.insert(name.toLower(), info); |
|
} |
|
|
|
// static |
|
const std::vector<std::string> &Profile::propertiesInfoList() |
|
{ |
|
static std::vector<std::string> list; |
|
if (!list.empty()) { |
|
return list; |
|
} |
|
|
|
list.reserve(DefaultProperties.size()); |
|
for (const PropertyInfo &info : DefaultProperties) { |
|
list.push_back(std::string(info.name) + " : " + info.defaultValue.typeName()); |
|
} |
|
return list; |
|
} |
|
|
|
const Profile::GroupPtr Profile::asGroup() const |
|
{ |
|
const Profile::GroupPtr ptr(dynamic_cast<ProfileGroup *>(const_cast<Profile *>(this))); |
|
return ptr; |
|
} |
|
|
|
Profile::GroupPtr Profile::asGroup() |
|
{ |
|
return Profile::GroupPtr(dynamic_cast<ProfileGroup *>(this)); |
|
} |
|
|
|
QString Profile::textEditorCmd() const |
|
{ |
|
auto current = property<int>(Profile::TextEditorCmd); |
|
|
|
QString editorCmd; |
|
switch (current) { |
|
case Enum::Kate: |
|
editorCmd = QStringLiteral("kate PATH:LINE:COLUMN"); |
|
break; |
|
case Enum::KWrite: |
|
editorCmd = QStringLiteral("kwrite PATH:LINE:COLUMN"); |
|
break; |
|
case Enum::KDevelop: |
|
editorCmd = QStringLiteral("kdevelop PATH:LINE:COLUMN"); |
|
break; |
|
case Enum::QtCreator: |
|
editorCmd = QStringLiteral("qtcreator PATH:LINE:COLUMN"); |
|
break; |
|
case Enum::Gedit: |
|
editorCmd = QStringLiteral("gedit +LINE:COLUMN PATH"); |
|
break; |
|
case Enum::gVim: |
|
editorCmd = QStringLiteral("gvim +LINE PATH"); |
|
break; |
|
case Enum::CustomTextEditor: |
|
editorCmd = customTextEditorCmd(); |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
return editorCmd; |
|
}
|
|
|