File indexing completed on 2024-04-28 05:50:44
0001 /* 0002 This source file is part of Konsole, a terminal emulator. 0003 0004 SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com> 0005 SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 // Own 0011 #include "Profile.h" 0012 0013 // Qt 0014 #include <QDir> 0015 #include <QFileInfo> 0016 #include <QTextCodec> 0017 0018 // KDE 0019 #include <KLocalizedString> 0020 #include <QFontDatabase> 0021 0022 // Konsole 0023 #include "Enumeration.h" 0024 #include "ProfileGroup.h" 0025 #include "config-konsole.h" 0026 0027 #ifndef Q_OS_WIN 0028 #ifdef HAVE_GETPWUID 0029 #include <pwd.h> 0030 #include <sys/types.h> 0031 #include <unistd.h> 0032 #endif // HAVE_GETPWUID 0033 #endif // Q_OS_WIN 0034 0035 #include <KSandbox> 0036 0037 using namespace Konsole; 0038 0039 #if defined(Q_OS_WIN) 0040 #define DEFAULT_ENCODING QStringLiteral("utf8") 0041 #else 0042 #define DEFAULT_ENCODING QString() 0043 #endif 0044 0045 // mappings between property enum values and names 0046 // 0047 // multiple names are defined for some property values, 0048 // in these cases, the "proper" string name comes first, 0049 // as that is used when reading/writing profiles from/to disk 0050 // 0051 // the other names are usually shorter versions for convenience 0052 // when parsing konsoleprofile commands 0053 static const char GENERAL_GROUP[] = "General"; 0054 static const char KEYBOARD_GROUP[] = "Keyboard"; 0055 static const char APPEARANCE_GROUP[] = "Appearance"; 0056 static const char SCROLLING_GROUP[] = "Scrolling"; 0057 static const char TERMINAL_GROUP[] = "Terminal Features"; 0058 static const char CURSOR_GROUP[] = "Cursor Options"; 0059 static const char INTERACTION_GROUP[] = "Interaction Options"; 0060 static const char ENCODING_GROUP[] = "Encoding Options"; 0061 0062 const std::vector<Profile::PropertyInfo> Profile::DefaultProperties = { 0063 // General 0064 {Path, "Path", nullptr, QString()}, 0065 {Name, "Name", GENERAL_GROUP, QString()}, 0066 {UntranslatedName, "UntranslatedName", nullptr, QString()}, 0067 {Icon, "Icon", GENERAL_GROUP, QLatin1String("utilities-terminal")}, 0068 {Command, "Command", nullptr, QString()}, 0069 {Arguments, "Arguments", nullptr, QStringList()}, 0070 {MenuIndex, "MenuIndex", nullptr, QLatin1String("0")}, 0071 {Environment, "Environment", GENERAL_GROUP, QStringList{QLatin1String("TERM=xterm-256color"), QLatin1String("COLORTERM=truecolor")}}, 0072 {Directory, "Directory", GENERAL_GROUP, QString()}, 0073 {LocalTabTitleFormat, "LocalTabTitleFormat", GENERAL_GROUP, QLatin1String("%d : %n")}, 0074 {LocalTabTitleFormat, "tabtitle", nullptr, QLatin1String("%d : %n")}, 0075 {RemoteTabTitleFormat, "RemoteTabTitleFormat", GENERAL_GROUP, QLatin1String("(%u) %H")}, 0076 {SemanticHints, "SemanticHints", GENERAL_GROUP, 1}, 0077 {SemanticUpDown, "SemanticUpDown", GENERAL_GROUP, false}, 0078 {SemanticInputClick, "SemanticInputClick", GENERAL_GROUP, false}, 0079 {ShowTerminalSizeHint, "ShowTerminalSizeHint", GENERAL_GROUP, true}, 0080 {StartInCurrentSessionDir, "StartInCurrentSessionDir", GENERAL_GROUP, true}, 0081 {SilenceSeconds, "SilenceSeconds", GENERAL_GROUP, 10}, 0082 {TerminalColumns, "TerminalColumns", GENERAL_GROUP, 110}, 0083 {TerminalRows, "TerminalRows", GENERAL_GROUP, 28}, 0084 {TerminalMargin, "TerminalMargin", GENERAL_GROUP, 1}, 0085 {TerminalCenter, "TerminalCenter", GENERAL_GROUP, false}, 0086 {ErrorBars, "ErrorBars", GENERAL_GROUP, 2}, 0087 {ErrorBackground, "ErrorBackground", GENERAL_GROUP, 1}, 0088 {AlternatingBars, "AlternatingBars", GENERAL_GROUP, 2}, 0089 {AlternatingBackground, "AlternatingBackground", GENERAL_GROUP, 1}, 0090 0091 // Appearance 0092 {Font, "Font", APPEARANCE_GROUP, QFont()}, 0093 {ColorScheme, "ColorScheme", APPEARANCE_GROUP, QLatin1String("Breeze")}, 0094 {ColorScheme, "colors", nullptr, QLatin1String("Breeze")}, 0095 {AntiAliasFonts, "AntiAliasFonts", APPEARANCE_GROUP, true}, 0096 {BoldIntense, "BoldIntense", APPEARANCE_GROUP, true}, 0097 {UseFontLineCharacters, "UseFontLineChararacters", APPEARANCE_GROUP, false}, 0098 {LineSpacing, "LineSpacing", APPEARANCE_GROUP, 0}, 0099 {TabColor, "TabColor", APPEARANCE_GROUP, QColor(QColor::Invalid)}, 0100 {DimValue, "DimmValue", APPEARANCE_GROUP, 128}, 0101 {DimWhenInactive, "DimWhenInactive", GENERAL_GROUP, false}, 0102 {BorderWhenActive, "BorderWhenActive", APPEARANCE_GROUP, false}, 0103 {FocusBorderColor, "FocusBorderColor", APPEARANCE_GROUP, QColor(Qt::gray)}, 0104 {InvertSelectionColors, "InvertSelectionColors", GENERAL_GROUP, false}, 0105 {EmojiFont, "EmojiFont", APPEARANCE_GROUP, QFont()}, 0106 {WordMode, "WordMode", APPEARANCE_GROUP, true}, 0107 {WordModeAttr, "WordModeAttr", APPEARANCE_GROUP, false}, 0108 {WordModeAscii, "WordModeAscii", APPEARANCE_GROUP, true}, 0109 {WordModeBrahmic, "WordModeBrahmic", APPEARANCE_GROUP, false}, 0110 {IgnoreWcWidth, "IgnoreWcWidth", APPEARANCE_GROUP, false}, 0111 0112 // Keyboard 0113 #ifdef Q_OS_MACOS 0114 {KeyBindings, "KeyBindings", KEYBOARD_GROUP, QLatin1String("macos")}, 0115 #else 0116 {KeyBindings, "KeyBindings", KEYBOARD_GROUP, QLatin1String("default")}, 0117 #endif 0118 0119 // Scrolling 0120 {HistoryMode, "HistoryMode", SCROLLING_GROUP, Enum::FixedSizeHistory}, 0121 {HistorySize, "HistorySize", SCROLLING_GROUP, 1000}, 0122 {ScrollBarPosition, "ScrollBarPosition", SCROLLING_GROUP, Enum::ScrollBarRight}, 0123 {ScrollFullPage, "ScrollFullPage", SCROLLING_GROUP, false}, 0124 {HighlightScrolledLines, "HighlightScrolledLines", SCROLLING_GROUP, true}, 0125 {ReflowLines, "ReflowLines", SCROLLING_GROUP, true}, 0126 0127 // Terminal Features 0128 {UrlHintsModifiers, "UrlHintsModifiers", TERMINAL_GROUP, 0}, 0129 {ReverseUrlHints, "ReverseUrlHints", TERMINAL_GROUP, false}, 0130 {BlinkingTextEnabled, "BlinkingTextEnabled", TERMINAL_GROUP, true}, 0131 {FlowControlEnabled, "FlowControlEnabled", TERMINAL_GROUP, true}, 0132 {BidiRenderingEnabled, "BidiRenderingEnabled", TERMINAL_GROUP, true}, 0133 {BidiLineLTR, "BidiLineLTR", TERMINAL_GROUP, true}, 0134 {BidiTableDirOverride, "BidiTableDirOverride", TERMINAL_GROUP, true}, 0135 {BlinkingCursorEnabled, "BlinkingCursorEnabled", TERMINAL_GROUP, false}, 0136 {BellMode, "BellMode", TERMINAL_GROUP, Enum::NotifyBell}, 0137 {VerticalLine, "VerticalLine", TERMINAL_GROUP, false}, 0138 {VerticalLineAtChar, "VerticalLineAtChar", TERMINAL_GROUP, 80}, 0139 {PeekPrimaryKeySequence, "PeekPrimaryKeySequence", TERMINAL_GROUP, QString()}, 0140 {LineNumbers, "LineNumbers", TERMINAL_GROUP, 0}, 0141 0142 // Cursor 0143 {UseCustomCursorColor, "UseCustomCursorColor", CURSOR_GROUP, false}, 0144 {CursorShape, "CursorShape", CURSOR_GROUP, Enum::BlockCursor}, 0145 {CustomCursorColor, "CustomCursorColor", CURSOR_GROUP, QColor(Qt::white)}, 0146 {CustomCursorTextColor, "CustomCursorTextColor", CURSOR_GROUP, QColor(Qt::black)}, 0147 0148 // Interaction 0149 {WordCharacters, "WordCharacters", INTERACTION_GROUP, QLatin1String(":@-./_~?&=%+#")}, 0150 {TripleClickMode, "TripleClickMode", INTERACTION_GROUP, Enum::SelectWholeLine}, 0151 {UnderlineLinksEnabled, "UnderlineLinksEnabled", INTERACTION_GROUP, true}, 0152 {UnderlineFilesEnabled, "UnderlineFilesEnabled", INTERACTION_GROUP, false}, 0153 {OpenLinksByDirectClickEnabled, "OpenLinksByDirectClickEnabled", INTERACTION_GROUP, false}, 0154 {TextEditorCmd, "TextEditorCmd", INTERACTION_GROUP, Enum::Kate}, 0155 {TextEditorCmdCustom, "TextEditorCmdCustom", INTERACTION_GROUP, QLatin1String("kate PATH:LINE:COLUMN")}, 0156 {CtrlRequiredForDrag, "CtrlRequiredForDrag", INTERACTION_GROUP, true}, 0157 {DropUrlsAsText, "DropUrlsAsText", INTERACTION_GROUP, true}, 0158 {AutoCopySelectedText, "AutoCopySelectedText", INTERACTION_GROUP, false}, 0159 {CopyTextAsHTML, "CopyTextAsHTML", INTERACTION_GROUP, true}, 0160 {TrimLeadingSpacesInSelectedText, "TrimLeadingSpacesInSelectedText", INTERACTION_GROUP, false}, 0161 {TrimTrailingSpacesInSelectedText, "TrimTrailingSpacesInSelectedText", INTERACTION_GROUP, false}, 0162 {PasteFromSelectionEnabled, "PasteFromSelectionEnabled", INTERACTION_GROUP, true}, 0163 {PasteFromClipboardEnabled, "PasteFromClipboardEnabled", INTERACTION_GROUP, false}, 0164 {MiddleClickPasteMode, "MiddleClickPasteMode", INTERACTION_GROUP, Enum::PasteFromX11Selection}, 0165 {MouseWheelZoomEnabled, "MouseWheelZoomEnabled", INTERACTION_GROUP, true}, 0166 {AllowMouseTracking, "AllowMouseTracking", INTERACTION_GROUP, true}, 0167 {AlternateScrolling, "AlternateScrolling", INTERACTION_GROUP, true}, 0168 {AllowEscapedLinks, "AllowEscapedLinks", INTERACTION_GROUP, false}, 0169 {EscapedLinksSchema, "EscapedLinksSchema", INTERACTION_GROUP, QLatin1String("http://;https://;file://")}, 0170 {ColorFilterEnabled, "ColorFilterEnabled", INTERACTION_GROUP, true}, 0171 0172 // Encoding 0173 {DefaultEncoding, "DefaultEncoding", ENCODING_GROUP, DEFAULT_ENCODING}, 0174 }; 0175 0176 QHash<QString, Profile::PropertyInfo> Profile::PropertyInfoByName; 0177 0178 // Magic path for the built-in profile which is not a valid file name, 0179 // thus it can not interfere with regular profiles. 0180 // For backward compatibility with existing profiles, it should never change. 0181 static const QString BUILTIN_MAGIC_PATH = QStringLiteral("FALLBACK/"); 0182 0183 #ifdef Q_OS_WIN 0184 static QString checkFile(const QStringList &dirList, const QString &filePath) 0185 { 0186 for (const QString &root : dirList) { 0187 QFileInfo info(root, filePath); 0188 if (info.exists()) { 0189 return QDir::toNativeSeparators(info.filePath()); 0190 } 0191 } 0192 return QString(); 0193 } 0194 0195 static QString GetWindowPowerShell() 0196 { 0197 QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); 0198 QStringList dirList; 0199 dirList << env.value(QStringLiteral("windir"), QStringLiteral("C:\\Windows")); 0200 return checkFile(dirList, QStringLiteral("System32\\WindowsPowerShell\\v1.0\\powershell.exe")); 0201 } 0202 0203 static QString GetWindowsShell() 0204 { 0205 QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); 0206 QString windir = env.value(QStringLiteral("windir"), QStringLiteral("C:\\Windows")); 0207 QFileInfo info(windir, QStringLiteral("System32\\cmd.exe")); 0208 return QDir::toNativeSeparators(info.filePath()); 0209 } 0210 #endif 0211 0212 static QString defaultShell() 0213 { 0214 #ifndef Q_OS_WIN 0215 if (!KSandbox::isFlatpak()) { 0216 return QString::fromUtf8(qgetenv("SHELL")); 0217 } 0218 0219 #ifdef HAVE_GETPWUID 0220 auto pw = getpwuid(getuid()); 0221 // pw: Do not pass the returned pointer to free. 0222 if (pw != nullptr) { 0223 QProcess proc; 0224 proc.setProgram(QStringLiteral("getent")); 0225 proc.setArguments({QStringLiteral("passwd"), QString::number(pw->pw_uid)}); 0226 KSandbox::startHostProcess(proc); 0227 proc.waitForFinished(); 0228 const auto output = proc.readAllStandardOutput().simplified(); 0229 if (auto parts = QString::fromUtf8(output).split(QLatin1Char(':')); output.size() >= 7) { 0230 return parts.at(6); 0231 } 0232 } 0233 return {}; 0234 #endif // HAVE_GETPWUID 0235 0236 #else // Q_OS_WIN 0237 auto shell = GetWindowPowerShell(); 0238 if (shell.isEmpty()) { 0239 shell = GetWindowsShell(); 0240 } 0241 return shell; 0242 #endif // Q_OS_WIN 0243 } 0244 0245 void Profile::fillTableWithDefaultNames() 0246 { 0247 static bool filledDefaults = false; 0248 0249 if (filledDefaults) { 0250 return; 0251 } 0252 0253 std::for_each(DefaultProperties.cbegin(), DefaultProperties.cend(), Profile::registerProperty); 0254 0255 filledDefaults = true; 0256 } 0257 0258 void Profile::useBuiltin() 0259 { 0260 for (const PropertyInfo &propInfo : DefaultProperties) { 0261 setProperty(propInfo.property, propInfo.defaultValue); 0262 } 0263 setProperty(Name, i18nc("Name of the built-in profile", "Built-in")); 0264 setProperty(UntranslatedName, QStringLiteral("Built-in")); 0265 setProperty(Path, BUILTIN_MAGIC_PATH); 0266 setProperty(Command, defaultShell()); 0267 // See Pty.cpp on why Arguments is populated 0268 setProperty(Arguments, QStringList{defaultShell()}); 0269 setProperty(Font, QFontDatabase::systemFont(QFontDatabase::FixedFont)); 0270 #if defined(Q_OS_WIN) 0271 setProperty(DefaultEncoding, QLatin1String(QTextCodec::codecForName("utf8")->name())); 0272 #else 0273 setProperty(DefaultEncoding, QLatin1String(QTextCodec::codecForLocale()->name())); 0274 #endif 0275 // Built-in profile should not be shown in menus 0276 setHidden(true); 0277 } 0278 0279 Profile::Profile(const Profile::Ptr &parent) 0280 : _propertyValues(PropertyMap()) 0281 , _parent(parent) 0282 , _hidden(false) 0283 { 0284 } 0285 0286 void Profile::clone(Profile::Ptr profile, bool differentOnly) 0287 { 0288 for (const PropertyInfo &info : DefaultProperties) { 0289 Property current = info.property; 0290 QVariant otherValue = profile->property<QVariant>(current); 0291 if (current == Name || current == Path) { // These are unique per Profile 0292 continue; 0293 } 0294 if (!differentOnly || property<QVariant>(current) != otherValue) { 0295 setProperty(current, otherValue); 0296 } 0297 } 0298 } 0299 0300 Profile::~Profile() = default; 0301 0302 bool Profile::isBuiltin() const 0303 { 0304 return path() == BUILTIN_MAGIC_PATH; 0305 } 0306 0307 bool Profile::isEditable() const 0308 { 0309 // Read-only profiles (i.e. with non-user-writable .profile location) 0310 // aren't editable. This includes the built-in profile, which is hardcoded. 0311 return !isBuiltin() && QFileInfo(path()).isWritable(); 0312 } 0313 0314 bool Profile::isDeletable() const 0315 { 0316 // To delete a file, parent dir must be writable 0317 const QFileInfo fileInfo(path()); 0318 return !isBuiltin() && fileInfo.exists() && QFileInfo(fileInfo.path()).isWritable(); 0319 } 0320 0321 bool Profile::isHidden() const 0322 { 0323 return _hidden; 0324 } 0325 void Profile::setHidden(bool hidden) 0326 { 0327 _hidden = hidden; 0328 } 0329 0330 void Profile::setParent(const Profile::Ptr &parent) 0331 { 0332 _parent = parent; 0333 } 0334 const Profile::Ptr Profile::parent() const 0335 { 0336 return _parent; 0337 } 0338 0339 bool Profile::isEmpty() const 0340 { 0341 return _propertyValues.empty(); 0342 } 0343 0344 const Profile::PropertyMap &Profile::properties() const 0345 { 0346 return _propertyValues; 0347 } 0348 0349 void Profile::setProperty(Property p, const QVariant &value) 0350 { 0351 _propertyValues.insert_or_assign(p, value); 0352 } 0353 0354 void Profile::setProperty(Property p, QVariant &&value) 0355 { 0356 _propertyValues.insert_or_assign(p, value); 0357 } 0358 0359 void Profile::assignProperties(const PropertyMap &map) 0360 { 0361 for (const auto &[p, value] : map) { 0362 setProperty(p, value); 0363 } 0364 } 0365 0366 void Profile::assignProperties(PropertyMap &&map) 0367 { 0368 // If a key exists in both maps, we want to use the associated 0369 // value from 'map' 0370 map.merge(_propertyValues); 0371 _propertyValues.swap(map); 0372 } 0373 0374 bool Profile::isPropertySet(Property p) const 0375 { 0376 return _propertyValues.find(p) != _propertyValues.cend(); 0377 } 0378 0379 Profile::Property Profile::lookupByName(const QString &name) 0380 { 0381 // insert default names into table the first time this is called 0382 fillTableWithDefaultNames(); 0383 0384 return PropertyInfoByName[name.toLower()].property; 0385 } 0386 0387 void Profile::registerProperty(const PropertyInfo &info) 0388 { 0389 QString name = QLatin1String(info.name); 0390 PropertyInfoByName.insert(name.toLower(), info); 0391 } 0392 0393 // static 0394 const std::vector<std::string> &Profile::propertiesInfoList() 0395 { 0396 static std::vector<std::string> list; 0397 if (!list.empty()) { 0398 return list; 0399 } 0400 0401 list.reserve(DefaultProperties.size()); 0402 for (const PropertyInfo &info : DefaultProperties) { 0403 list.push_back(std::string(info.name) + " : " + info.defaultValue.typeName()); 0404 } 0405 return list; 0406 } 0407 0408 const Profile::GroupPtr Profile::asGroup() const 0409 { 0410 const Profile::GroupPtr ptr(dynamic_cast<ProfileGroup *>(const_cast<Profile *>(this))); 0411 return ptr; 0412 } 0413 0414 Profile::GroupPtr Profile::asGroup() 0415 { 0416 return Profile::GroupPtr(dynamic_cast<ProfileGroup *>(this)); 0417 } 0418 0419 QString Profile::textEditorCmd() const 0420 { 0421 auto current = property<int>(Profile::TextEditorCmd); 0422 0423 QString editorCmd; 0424 switch (current) { 0425 case Enum::Kate: 0426 editorCmd = QStringLiteral("kate PATH:LINE:COLUMN"); 0427 break; 0428 case Enum::KWrite: 0429 editorCmd = QStringLiteral("kwrite PATH:LINE:COLUMN"); 0430 break; 0431 case Enum::KDevelop: 0432 editorCmd = QStringLiteral("kdevelop PATH:LINE:COLUMN"); 0433 break; 0434 case Enum::QtCreator: 0435 editorCmd = QStringLiteral("qtcreator PATH:LINE:COLUMN"); 0436 break; 0437 case Enum::Gedit: 0438 editorCmd = QStringLiteral("gedit +LINE:COLUMN PATH"); 0439 break; 0440 case Enum::gVim: 0441 editorCmd = QStringLiteral("gvim +LINE PATH"); 0442 break; 0443 case Enum::CustomTextEditor: 0444 editorCmd = customTextEditorCmd(); 0445 break; 0446 default: 0447 break; 0448 } 0449 0450 return editorCmd; 0451 } 0452 0453 #include "moc_Profile.cpp"