mirror of
https://github.com/KDE/konsole.git
synced 2026-04-29 02:07:29 -04:00
2256 lines
92 KiB
C++
2256 lines
92 KiB
C++
/*
|
|
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 "EditProfileDialog.h"
|
|
|
|
// Qt
|
|
#include <QColor>
|
|
#include <QDialog>
|
|
#include <QDialogButtonBox>
|
|
#include <QFileDialog>
|
|
#include <QIcon>
|
|
#include <QInputDialog>
|
|
#include <QMenu>
|
|
#include <QPushButton>
|
|
#include <QRegularExpressionValidator>
|
|
#include <QSlider>
|
|
#include <QStandardItem>
|
|
#include <QStandardPaths>
|
|
#include <QStringListModel>
|
|
#include <QTimer>
|
|
#include <QUrl>
|
|
|
|
// KDE
|
|
#include <KCodecAction>
|
|
#include <KColorButton>
|
|
#include <KIconDialog>
|
|
#include <KLocalizedString>
|
|
#include <KWindowSystem>
|
|
|
|
#include <KNSWidgets/Button>
|
|
|
|
// Konsole
|
|
#include "ui_EditProfileAdvancedPage.h"
|
|
#include "ui_EditProfileAppearancePage.h"
|
|
#include "ui_EditProfileGeneralPage.h"
|
|
#include "ui_EditProfileKeyboardPage.h"
|
|
#include "ui_EditProfileMousePage.h"
|
|
#include "ui_EditProfileScrollingPage.h"
|
|
#include "ui_EditProfileTabsPage.h"
|
|
|
|
#include "colorscheme/ColorSchemeManager.h"
|
|
|
|
#include "keyboardtranslator/KeyboardTranslator.h"
|
|
|
|
#include "KeyBindingEditor.h"
|
|
#include "ShellCommand.h"
|
|
#include "WindowSystemInfo.h"
|
|
#include "profile/ProfileManager.h"
|
|
|
|
// Others
|
|
#if defined(Q_OS_LINUX) | defined(Q_OS_FREEBSD) | defined(Q_OS_OPENBSD) | defined(Q_OS_SOLARIS) | defined(Q_OS_MACOS)
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
using namespace Konsole;
|
|
|
|
EditProfileDialog::EditProfileDialog(QWidget *parent)
|
|
: KPageDialog(parent)
|
|
, _tempProfile(new Profile{})
|
|
{
|
|
_tempProfile->setHidden(true);
|
|
|
|
setWindowTitle(i18n("Edit Profile"));
|
|
setFaceType(KPageDialog::List);
|
|
|
|
_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply);
|
|
setButtonBox(_buttonBox);
|
|
|
|
_buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
|
|
|
|
auto *applyButton = _buttonBox->button(QDialogButtonBox::Apply);
|
|
// Disable it, since no modifications have been made yet
|
|
applyButton->setEnabled(false);
|
|
connect(applyButton, &QPushButton::clicked, this, [this]() {
|
|
if (isProfileNameValid()) {
|
|
save();
|
|
}
|
|
});
|
|
|
|
// Set a fallback icon for non-plasma desktops as this dialog looks
|
|
// terrible without all the icons on the left sidebar. On GTK related
|
|
// desktops, this dialog look good enough without installing
|
|
// oxygen-icon-theme, qt5ct and setting export QT_QPA_PLATFORMTHEME=qt5ct
|
|
// Plain Xorg desktops still look terrible as there are no icons
|
|
// visible.
|
|
const auto defaultIcon = QIcon::fromTheme(QStringLiteral("utilities-terminal"));
|
|
|
|
// General page
|
|
|
|
const QString generalPageName = i18nc("@title:tab Generic, common options", "General");
|
|
auto *generalPageWidget = new QWidget(this);
|
|
_generalUi = new Ui::EditProfileGeneralPage();
|
|
_generalUi->setupUi(generalPageWidget);
|
|
_generalPageItem = addPage(generalPageWidget, generalPageName);
|
|
_generalPageItem->setHeader(generalPageName);
|
|
_generalPageItem->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal")));
|
|
_pages[_generalPageItem] = Page(&EditProfileDialog::setupGeneralPage);
|
|
|
|
// Tabs page
|
|
|
|
const QString tabsPageName = i18n("Tabs");
|
|
auto *tabsPageWidget = new QWidget(this);
|
|
_tabsUi = new Ui::EditProfileTabsPage();
|
|
_tabsUi->setupUi(tabsPageWidget);
|
|
auto *tabsPageItem = addPage(tabsPageWidget, tabsPageName);
|
|
tabsPageItem->setHeader(tabsPageName);
|
|
tabsPageItem->setIcon(QIcon::fromTheme(QStringLiteral("preferences-tabs"), defaultIcon));
|
|
_pages[tabsPageItem] = Page(&EditProfileDialog::setupTabsPage);
|
|
|
|
LabelsAligner tabsAligner(tabsPageWidget);
|
|
tabsAligner.addLayout(dynamic_cast<QGridLayout *>(_tabsUi->tabMonitoringGroup->layout()));
|
|
tabsAligner.addLayout(dynamic_cast<QGridLayout *>(_tabsUi->renameTabWidget->layout()));
|
|
tabsAligner.updateLayouts();
|
|
tabsAligner.align();
|
|
|
|
// Appearance page
|
|
|
|
const QString appearancePageName = i18n("Appearance");
|
|
auto *appearancePageWidget = new QWidget(this);
|
|
_appearanceUi = new Ui::EditProfileAppearancePage();
|
|
_appearanceUi->setupUi(appearancePageWidget);
|
|
auto *appearancePageItem = addPage(appearancePageWidget, appearancePageName);
|
|
appearancePageItem->setHeader(appearancePageName);
|
|
appearancePageItem->setIcon(QIcon::fromTheme(QStringLiteral("kcolorchooser"), defaultIcon));
|
|
_pages[appearancePageItem] = Page(&EditProfileDialog::setupAppearancePage);
|
|
|
|
LabelsAligner appearanceAligner(appearancePageWidget);
|
|
appearanceAligner.addLayout(dynamic_cast<QGridLayout *>(_appearanceUi->contentsGroup->layout()));
|
|
appearanceAligner.updateLayouts();
|
|
appearanceAligner.align();
|
|
|
|
// Scrolling page
|
|
|
|
const QString scrollingPageName = i18n("Scrolling");
|
|
auto *scrollingPageWidget = new QWidget(this);
|
|
_scrollingUi = new Ui::EditProfileScrollingPage();
|
|
_scrollingUi->setupUi(scrollingPageWidget);
|
|
auto *scrollingPageItem = addPage(scrollingPageWidget, scrollingPageName);
|
|
scrollingPageItem->setHeader(scrollingPageName);
|
|
scrollingPageItem->setIcon(QIcon::fromTheme(QStringLiteral("preferences-scroll"), defaultIcon));
|
|
_pages[scrollingPageItem] = Page(&EditProfileDialog::setupScrollingPage);
|
|
|
|
// adjust "history size" label height to match history size widget's first radio button
|
|
_scrollingUi->historySizeLabel->setFixedHeight(_scrollingUi->historySizeWidget->preferredLabelHeight());
|
|
|
|
// Keyboard page
|
|
|
|
const QString keyboardPageName = i18n("Keyboard");
|
|
const QString keyboardPageTitle = i18n("Key bindings");
|
|
auto *keyboardPageWidget = new QWidget(this);
|
|
_keyboardUi = new Ui::EditProfileKeyboardPage();
|
|
_keyboardUi->setupUi(keyboardPageWidget);
|
|
auto *keyboardPageItem = addPage(keyboardPageWidget, keyboardPageName);
|
|
keyboardPageItem->setHeader(keyboardPageTitle);
|
|
keyboardPageItem->setIcon(QIcon::fromTheme(QStringLiteral("input-keyboard"), defaultIcon));
|
|
_pages[keyboardPageItem] = Page(&EditProfileDialog::setupKeyboardPage);
|
|
|
|
// Mouse page
|
|
|
|
const QString mousePageName = i18n("Mouse");
|
|
auto *mousePageWidget = new QWidget(this);
|
|
_mouseUi = new Ui::EditProfileMousePage();
|
|
_mouseUi->setupUi(mousePageWidget);
|
|
|
|
const auto regExp = QRegularExpression(QStringLiteral(R"(([a-z]*:\/\/;)*([A-Za-z*]:\/\/))"));
|
|
auto validator = new QRegularExpressionValidator(regExp, this);
|
|
_mouseUi->linkEscapeSequenceTexts->setValidator(validator);
|
|
|
|
auto *mousePageItem = addPage(mousePageWidget, mousePageName);
|
|
mousePageItem->setHeader(mousePageName);
|
|
mousePageItem->setIcon(QIcon::fromTheme(QStringLiteral("input-mouse"), defaultIcon));
|
|
_pages[mousePageItem] = Page(&EditProfileDialog::setupMousePage);
|
|
|
|
// Advanced page
|
|
|
|
const QString advancedPageName = i18nc("@title:tab Complex options", "Advanced");
|
|
auto *advancedPageWidget = new QWidget(this);
|
|
_advancedUi = new Ui::EditProfileAdvancedPage();
|
|
_advancedUi->setupUi(advancedPageWidget);
|
|
auto *advancedPageItem = addPage(advancedPageWidget, advancedPageName);
|
|
advancedPageItem->setHeader(advancedPageName);
|
|
advancedPageItem->setIcon(QIcon::fromTheme(QStringLiteral("preferences-other"), defaultIcon));
|
|
_pages[advancedPageItem] = Page(&EditProfileDialog::setupAdvancedPage);
|
|
|
|
// there are various setupXYZPage() methods to load the items
|
|
// for each page and update their states to match the profile
|
|
// being edited.
|
|
//
|
|
// these are only called when needed ( ie. when the user clicks
|
|
// the tab to move to that page ).
|
|
//
|
|
// the _pageNeedsUpdate vector keeps track of the pages that have
|
|
// not been updated since the last profile change and will need
|
|
// to be refreshed when the user switches to them
|
|
connect(this, &KPageDialog::currentPageChanged, this, &Konsole::EditProfileDialog::preparePage);
|
|
}
|
|
|
|
EditProfileDialog::~EditProfileDialog()
|
|
{
|
|
delete _generalUi;
|
|
delete _tabsUi;
|
|
delete _appearanceUi;
|
|
delete _scrollingUi;
|
|
delete _keyboardUi;
|
|
delete _mouseUi;
|
|
delete _advancedUi;
|
|
}
|
|
|
|
void EditProfileDialog::save()
|
|
{
|
|
const bool isNewProfile = _profileState == EditProfileDialog::NewProfile;
|
|
|
|
if (isNewProfile) {
|
|
ProfileManager::instance()->addProfile(_profile);
|
|
// The profile exists now, no longer treat it as initial creation
|
|
_profileState = EditProfileDialog::ExistingProfile;
|
|
}
|
|
|
|
bool defaultChanged = _isDefault != _generalUi->setAsDefaultButton->isChecked();
|
|
|
|
if (_tempProfile->isEmpty() && !defaultChanged) {
|
|
if (isNewProfile) {
|
|
// New profile, we need to save it to disk, even if no settings
|
|
// were changed and _tempProfile is empty
|
|
ProfileManager::instance()->changeProfile(_profile, _profile->properties());
|
|
}
|
|
// no changes since last save
|
|
return;
|
|
}
|
|
|
|
ProfileManager::instance()->changeProfile(_profile, _tempProfile->properties());
|
|
|
|
// ensure that these settings are not undone by a call
|
|
// to unpreview()
|
|
_previewedProperties.clear();
|
|
|
|
// Update the default profile if needed
|
|
if (defaultChanged) {
|
|
Q_ASSERT(_profile != ProfileManager::instance()->builtinProfile());
|
|
|
|
bool defaultChecked = _generalUi->setAsDefaultButton->isChecked();
|
|
Profile::Ptr newDefault = defaultChecked ? _profile : ProfileManager::instance()->builtinProfile();
|
|
ProfileManager::instance()->setDefaultProfile(newDefault);
|
|
_isDefault = defaultChecked;
|
|
}
|
|
|
|
resetTempProfile();
|
|
|
|
_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
|
|
}
|
|
|
|
void EditProfileDialog::reject()
|
|
{
|
|
unpreviewAll();
|
|
QDialog::reject();
|
|
}
|
|
|
|
void EditProfileDialog::accept()
|
|
{
|
|
if (isProfileNameValid()) {
|
|
save();
|
|
unpreviewAll();
|
|
QDialog::accept();
|
|
}
|
|
}
|
|
|
|
void EditProfileDialog::setMessageGeneralPage(const QString &msg)
|
|
{
|
|
_generalUi->generalPageMessageWidget->setText(msg);
|
|
_generalUi->generalPageMessageWidget->setMessageType(KMessageWidget::Error);
|
|
setCurrentPage(_generalPageItem);
|
|
_generalUi->generalPageMessageWidget->animatedShow();
|
|
}
|
|
|
|
bool EditProfileDialog::isProfileNameValid()
|
|
{
|
|
Q_ASSERT(_profile);
|
|
Q_ASSERT(_tempProfile);
|
|
|
|
// check whether the user has enough permissions to save the profile
|
|
QFileInfo fileInfo(_profile->path());
|
|
if (fileInfo.exists() && !fileInfo.isWritable() && (!_tempProfile->isPropertySet(Profile::Name) || _tempProfile->name() == _profile->name())) {
|
|
setMessageGeneralPage(xi18nc("@info",
|
|
"Insufficient permissions to save settings to: <filename>%1</filename>.<nl/>"
|
|
"Either change the permissions of that file or set a different name to save "
|
|
"the settings to a new profile.",
|
|
_profile->path()));
|
|
return false;
|
|
}
|
|
|
|
const QList<Profile::Ptr> existingProfiles = ProfileManager::instance()->allProfiles();
|
|
QStringList otherExistingProfileNames;
|
|
|
|
for (const Profile::Ptr &existingProfile : existingProfiles) {
|
|
if (existingProfile->name() != _profile->name()) {
|
|
otherExistingProfileNames.append(existingProfile->name());
|
|
}
|
|
}
|
|
|
|
if ((_tempProfile->isPropertySet(Profile::Name) && _tempProfile->name().isEmpty()) || (_profile->name().isEmpty() && _tempProfile->name().isEmpty())) {
|
|
setMessageGeneralPage(i18nc("@info", "Profile Name was empty; please set a name to be able to save settings."));
|
|
// Revert the name in the dialog
|
|
_generalUi->profileNameEdit->setText(_profile->name());
|
|
selectProfileName();
|
|
return false;
|
|
}
|
|
|
|
if (!_tempProfile->name().isEmpty() && otherExistingProfileNames.contains(_tempProfile->name())) {
|
|
setMessageGeneralPage(i18nc("@info", "A profile with the name \"%1\" already exists.", _generalUi->profileNameEdit->text()));
|
|
// Revert the name in the dialog
|
|
_generalUi->profileNameEdit->setText(_profile->name());
|
|
selectProfileName();
|
|
return false;
|
|
}
|
|
|
|
const QString profileStoragePath(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + QStringLiteral("konsole"));
|
|
int nameLenMax = 256;
|
|
|
|
#if defined(Q_OS_LINUX) | defined(Q_OS_OPENBSD) | defined(Q_OS_FREEBSD) | defined(Q_OS_MACOS) | defined(Q_OS_SOLARIS)
|
|
const int maxPath = pathconf(profileStoragePath.toLocal8Bit().constData(), _PC_PATH_MAX);
|
|
const int nameLenMaxPath = maxPath - _profile->path().lastIndexOf(QLatin1Char('/')) + 1;
|
|
const int nameLenMaxName = pathconf(profileStoragePath.toLocal8Bit().constData(), _PC_NAME_MAX);
|
|
nameLenMax = qMin(nameLenMaxPath, nameLenMaxName);
|
|
#elif defined(Q_OS_WIN)
|
|
// If MAX_PATH not found, use default (260 - 1)
|
|
const int maxPath = qMax(259, qEnvironmentVariable("MAX_PATH").toInt());
|
|
nameLenMax = maxPath - _profile->path().lastIndexOf(QLatin1Char('/')) + 1;
|
|
#endif
|
|
|
|
nameLenMax -= QStringLiteral(".profile").size();
|
|
|
|
// doesn't work on macOS and maybe other systems so make sure we have a sane value always
|
|
// See https://mail.kde.org/pipermail/kwrite-devel/2024-April/007139.html
|
|
if (nameLenMax < 0) {
|
|
nameLenMax = 100;
|
|
}
|
|
|
|
if (_tempProfile->name().size() > nameLenMax) {
|
|
setMessageGeneralPage(i18nc("@info", "Profile name exceeded maximum allowed length (%1).", nameLenMax));
|
|
// Revert the name in the dialog
|
|
_generalUi->profileNameEdit->setText(_profile->name());
|
|
selectProfileName();
|
|
return false;
|
|
}
|
|
|
|
if (!_tempProfile->name().isEmpty()) {
|
|
static const QString illegalCharacters(QStringLiteral("#%&{}/\\\"<>*?$!':@`+=|"));
|
|
static const QString colorTagOpen(QStringLiteral("<font color=\"red\">"));
|
|
static const QString colorTagClose(QStringLiteral("</font>"));
|
|
const QString name(_tempProfile->name());
|
|
bool hasIllegal = false;
|
|
bool unclosedTag = false;
|
|
QString highlightedName;
|
|
|
|
for (int i = 0; i < name.size(); ++i) {
|
|
const auto currChar = name.at(i);
|
|
|
|
if (illegalCharacters.contains(currChar)) {
|
|
if (!unclosedTag) {
|
|
highlightedName += colorTagOpen;
|
|
}
|
|
hasIllegal = true;
|
|
unclosedTag = true;
|
|
} else if (unclosedTag) {
|
|
unclosedTag = false;
|
|
highlightedName += colorTagClose;
|
|
}
|
|
|
|
highlightedName += currChar;
|
|
|
|
if (i + 1 == name.size() && unclosedTag) {
|
|
highlightedName += colorTagClose;
|
|
}
|
|
}
|
|
|
|
if (hasIllegal) {
|
|
setMessageGeneralPage(i18nc("@info", "The highlighted characters are invalid in profile names: %1.", highlightedName));
|
|
// Revert the name in the dialog
|
|
_generalUi->profileNameEdit->setText(_profile->name());
|
|
selectProfileName();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Valid name
|
|
return true;
|
|
}
|
|
|
|
QString EditProfileDialog::groupProfileNames(const ProfileGroup::Ptr &group, int maxLength)
|
|
{
|
|
QString caption;
|
|
int count = group->profiles().count();
|
|
for (int i = 0; i < count; i++) {
|
|
caption += group->profiles()[i]->name();
|
|
if (i < (count - 1)) {
|
|
caption += QLatin1Char(',');
|
|
// limit caption length to prevent very long window titles
|
|
if (maxLength > 0 && caption.length() > maxLength) {
|
|
caption += QLatin1String("...");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return caption;
|
|
}
|
|
|
|
void EditProfileDialog::updateCaption(const Profile::Ptr &profile)
|
|
{
|
|
const int MAX_GROUP_CAPTION_LENGTH = 25;
|
|
ProfileGroup::Ptr group = profile->asGroup();
|
|
if (group && group->profiles().count() > 1) {
|
|
QString caption = groupProfileNames(group, MAX_GROUP_CAPTION_LENGTH);
|
|
setWindowTitle(i18np("Editing profile: %2", "Editing %1 profiles: %2", group->profiles().count(), caption));
|
|
updateButtonApply();
|
|
} else {
|
|
if (_profileState == EditProfileDialog::NewProfile) {
|
|
setWindowTitle(i18n("Create New Profile"));
|
|
_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
|
|
} else {
|
|
setWindowTitle(i18n("Edit Profile \"%1\"", profile->name()));
|
|
updateButtonApply();
|
|
}
|
|
}
|
|
}
|
|
|
|
void EditProfileDialog::setProfile(const Konsole::Profile::Ptr &profile, EditProfileDialog::InitialProfileState state)
|
|
{
|
|
Q_ASSERT(profile);
|
|
|
|
_profile = profile;
|
|
|
|
_profileState = state;
|
|
|
|
// update caption
|
|
updateCaption(profile);
|
|
|
|
// mark each page of the dialog as out of date
|
|
// and force an update of the currently visible page
|
|
//
|
|
// the other pages will be updated as necessary
|
|
for (Page &page : _pages) {
|
|
page.needsUpdate = true;
|
|
}
|
|
preparePage(currentPage());
|
|
|
|
resetTempProfile();
|
|
}
|
|
|
|
const QString EditProfileDialog::currentColorSchemeName() const
|
|
{
|
|
const QString ¤tColorSchemeName = _profile->colorScheme();
|
|
return currentColorSchemeName;
|
|
}
|
|
|
|
void EditProfileDialog::preparePage(KPageWidgetItem *current, KPageWidgetItem *before)
|
|
{
|
|
Q_UNUSED(before)
|
|
Q_ASSERT(current);
|
|
Q_ASSERT(_pages.contains(current));
|
|
|
|
const Profile::Ptr profile = _profile;
|
|
auto setupPage = _pages[current].setupPage;
|
|
Q_ASSERT(profile);
|
|
Q_ASSERT(setupPage);
|
|
|
|
if (_pages[current].needsUpdate) {
|
|
(*this.*setupPage)(profile);
|
|
_pages[current].needsUpdate = false;
|
|
}
|
|
}
|
|
|
|
void Konsole::EditProfileDialog::selectProfileName()
|
|
{
|
|
_generalUi->profileNameEdit->setFocus();
|
|
_generalUi->profileNameEdit->selectAll();
|
|
}
|
|
|
|
void EditProfileDialog::setupGeneralPage(const Profile::Ptr &profile)
|
|
{
|
|
_generalUi->generalPageMessageWidget->setVisible(false);
|
|
_generalUi->generalPageMessageWidget->setWordWrap(true);
|
|
_generalUi->generalPageMessageWidget->setCloseButtonVisible(true);
|
|
|
|
// basic profile options
|
|
{
|
|
ProfileGroup::Ptr group = profile->asGroup();
|
|
if (!group || group->profiles().count() < 2) {
|
|
_generalUi->profileNameEdit->setText(profile->name());
|
|
_generalUi->profileNameEdit->setClearButtonEnabled(true);
|
|
} else {
|
|
_generalUi->profileNameEdit->setText(groupProfileNames(group, -1));
|
|
_generalUi->profileNameEdit->setEnabled(false);
|
|
}
|
|
}
|
|
|
|
ShellCommand command(profile->command(), profile->arguments());
|
|
_generalUi->commandEdit->setText(command.fullCommand());
|
|
// If a "completion" is requested, consider changing this to KLineEdit
|
|
// and using KCompletion.
|
|
_generalUi->initialDirEdit->setText(profile->defaultWorkingDirectory());
|
|
_generalUi->initialDirEdit->setClearButtonEnabled(true);
|
|
_generalUi->initialDirEdit->setPlaceholderText(QStandardPaths::standardLocations(QStandardPaths::HomeLocation).value(0));
|
|
|
|
_generalUi->dirSelectButton->setIcon(QIcon::fromTheme(QStringLiteral("folder-open")));
|
|
_generalUi->iconSelectButton->setIcon(QIcon::fromTheme(profile->icon()));
|
|
_generalUi->environmentEditButton->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
|
|
_generalUi->startInSameDirButton->setChecked(profile->startInCurrentSessionDir());
|
|
|
|
// initial terminal size
|
|
const auto colsSuffix =
|
|
ki18ncp("Suffix of the number of columns (N columns). The leading space is needed to separate it from the number value.", " column", " columns");
|
|
const auto rowsSuffix =
|
|
ki18ncp("Suffix of the number of rows (N rows). The leading space is needed to separate it from the number value.", " row", " rows");
|
|
_generalUi->terminalColumnsEntry->setValue(profile->terminalColumns());
|
|
_generalUi->terminalRowsEntry->setValue(profile->terminalRows());
|
|
_generalUi->terminalColumnsEntry->setSuffix(colsSuffix);
|
|
_generalUi->terminalRowsEntry->setSuffix(rowsSuffix);
|
|
// make width of initial terminal size spinboxes equal
|
|
const int sizeEntryWidth = qMax(maxSpinBoxWidth(_generalUi->terminalColumnsEntry, colsSuffix), maxSpinBoxWidth(_generalUi->terminalRowsEntry, rowsSuffix));
|
|
_generalUi->terminalColumnsEntry->setFixedWidth(sizeEntryWidth);
|
|
_generalUi->terminalRowsEntry->setFixedWidth(sizeEntryWidth);
|
|
|
|
auto *bellModeModel = new QStringListModel({i18n("System Bell"), i18n("System Notifications"), i18n("Visual Bell"), i18n("Ignore Bell Events")}, this);
|
|
_generalUi->terminalBellCombo->setModel(bellModeModel);
|
|
_generalUi->terminalBellCombo->setCurrentIndex(profile->property<int>(Profile::BellMode));
|
|
|
|
_isDefault = profile == ProfileManager::instance()->defaultProfile();
|
|
_generalUi->setAsDefaultButton->setChecked(_isDefault);
|
|
QString appName = QCoreApplication::applicationName();
|
|
if (!appName.isEmpty() && appName != QLatin1String("konsole")) {
|
|
appName[0] = appName.at(0).toUpper();
|
|
_generalUi->setAsDefaultButton->setText(i18n("Default profile for new terminal sessions in %1", appName));
|
|
} else {
|
|
_generalUi->setAsDefaultButton->setText(i18n("Default profile"));
|
|
}
|
|
_generalUi->semanticUpDown->setChecked(profile->semanticUpDown());
|
|
_generalUi->semanticInputClick->setChecked(profile->semanticInputClick());
|
|
|
|
// signals and slots
|
|
connect(_generalUi->dirSelectButton, &QToolButton::clicked, this, &Konsole::EditProfileDialog::selectInitialDir);
|
|
connect(_generalUi->iconSelectButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::selectIcon);
|
|
connect(_generalUi->startInSameDirButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::startInSameDir);
|
|
connect(_generalUi->profileNameEdit, &QLineEdit::textChanged, this, &Konsole::EditProfileDialog::profileNameChanged);
|
|
connect(_generalUi->initialDirEdit, &QLineEdit::textChanged, this, &Konsole::EditProfileDialog::initialDirChanged);
|
|
connect(_generalUi->commandEdit, &QLineEdit::textChanged, this, &Konsole::EditProfileDialog::commandChanged);
|
|
connect(_generalUi->environmentEditButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::showEnvironmentEditor);
|
|
connect(_generalUi->semanticUpDown, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::semanticUpDown);
|
|
connect(_generalUi->semanticInputClick, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::semanticInputClick);
|
|
|
|
connect(_generalUi->terminalColumnsEntry, &QSpinBox::valueChanged, this, &Konsole::EditProfileDialog::terminalColumnsEntryChanged);
|
|
connect(_generalUi->terminalRowsEntry, &QSpinBox::valueChanged, this, &Konsole::EditProfileDialog::terminalRowsEntryChanged);
|
|
|
|
connect(_generalUi->terminalBellCombo, &QComboBox::currentIndexChanged, this, [this](const int index) {
|
|
updateTempProfileProperty(Profile::BellMode, index);
|
|
});
|
|
|
|
connect(_generalUi->setAsDefaultButton, &QAbstractButton::toggled, this, &Konsole::EditProfileDialog::updateButtonApply);
|
|
const ButtonGroupOptions semanticHints = {
|
|
_generalUi->semanticHints, // group
|
|
Profile::SemanticHints, // profileProperty
|
|
false, // preview
|
|
{
|
|
// buttons
|
|
{_generalUi->semanticHintsNever, Enum::HintsNever},
|
|
{_generalUi->semanticHintsURL, Enum::HintsURL},
|
|
{_generalUi->semanticHintsAlways, Enum::HintsAlways},
|
|
},
|
|
};
|
|
setupButtonGroup(semanticHints, profile);
|
|
const ButtonGroupOptions errorBars = {
|
|
_generalUi->errorBars, // group
|
|
Profile::ErrorBars, // profileProperty
|
|
false, // preview
|
|
{
|
|
// buttons
|
|
{_generalUi->errorBarsNever, Enum::HintsNever},
|
|
{_generalUi->errorBarsURL, Enum::HintsURL},
|
|
{_generalUi->errorBarsAlways, Enum::HintsAlways},
|
|
},
|
|
};
|
|
setupButtonGroup(errorBars, profile);
|
|
const ButtonGroupOptions errorBackground = {
|
|
_generalUi->errorBackground, // group
|
|
Profile::ErrorBackground, // profileProperty
|
|
false, // preview
|
|
{
|
|
// buttons
|
|
{_generalUi->errorBackgroundNever, Enum::HintsNever},
|
|
{_generalUi->errorBackgroundURL, Enum::HintsURL},
|
|
{_generalUi->errorBackgroundAlways, Enum::HintsAlways},
|
|
},
|
|
};
|
|
setupButtonGroup(errorBackground, profile);
|
|
const ButtonGroupOptions alternatingBars = {
|
|
_generalUi->alternatingBars, // group
|
|
Profile::AlternatingBars, // profileProperty
|
|
false, // preview
|
|
{
|
|
// buttons
|
|
{_generalUi->alternatingBarsNever, Enum::HintsNever},
|
|
{_generalUi->alternatingBarsURL, Enum::HintsURL},
|
|
{_generalUi->alternatingBarsAlways, Enum::HintsAlways},
|
|
},
|
|
};
|
|
setupButtonGroup(alternatingBars, profile);
|
|
const ButtonGroupOptions alternatingBackground = {
|
|
_generalUi->alternatingBackground, // group
|
|
Profile::AlternatingBackground, // profileProperty
|
|
false, // preview
|
|
{
|
|
// buttons
|
|
{_generalUi->alternatingBackgroundNever, Enum::HintsNever},
|
|
{_generalUi->alternatingBackgroundURL, Enum::HintsURL},
|
|
{_generalUi->alternatingBackgroundAlways, Enum::HintsAlways},
|
|
},
|
|
};
|
|
setupButtonGroup(alternatingBackground, profile);
|
|
}
|
|
|
|
void EditProfileDialog::showEnvironmentEditor()
|
|
{
|
|
bool ok;
|
|
const Profile::Ptr profile = _profile;
|
|
|
|
QStringList currentEnvironment;
|
|
|
|
// The user could re-open the environment editor before clicking
|
|
// OK/Apply in the parent edit profile dialog, so we make sure
|
|
// to show the new environment vars
|
|
if (_tempProfile->isPropertySet(Profile::Environment)) {
|
|
currentEnvironment = _tempProfile->environment();
|
|
} else {
|
|
currentEnvironment = profile->environment();
|
|
}
|
|
|
|
QString text = QInputDialog::getMultiLineText(this,
|
|
i18n("Edit Environment"),
|
|
i18n("One environment variable per line"),
|
|
currentEnvironment.join(QStringLiteral("\n")),
|
|
&ok);
|
|
|
|
QStringList newEnvironment;
|
|
|
|
if (ok) {
|
|
if (!text.isEmpty()) {
|
|
newEnvironment = text.split(QLatin1Char('\n'));
|
|
updateTempProfileProperty(Profile::Environment, newEnvironment);
|
|
} else {
|
|
// the user could have removed all entries so we return an empty list
|
|
updateTempProfileProperty(Profile::Environment, newEnvironment);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EditProfileDialog::setupTabsPage(const Profile::Ptr &profile)
|
|
{
|
|
// tab title format
|
|
_tabsUi->renameTabWidget->setTabTitleText(profile->localTabTitleFormat());
|
|
_tabsUi->renameTabWidget->setRemoteTabTitleText(profile->remoteTabTitleFormat());
|
|
_tabsUi->renameTabWidget->setColor(profile->tabColor());
|
|
|
|
connect(_tabsUi->renameTabWidget, &Konsole::RenameTabWidget::tabTitleFormatChanged, this, &Konsole::EditProfileDialog::tabTitleFormatChanged);
|
|
connect(_tabsUi->renameTabWidget, &Konsole::RenameTabWidget::remoteTabTitleFormatChanged, this, &Konsole::EditProfileDialog::remoteTabTitleFormatChanged);
|
|
connect(_tabsUi->renameTabWidget, &Konsole::RenameTabWidget::tabColorChanged, this, &Konsole::EditProfileDialog::tabColorChanged);
|
|
|
|
// tab monitoring
|
|
const int silenceSeconds = profile->silenceSeconds();
|
|
_tabsUi->silenceSecondsSpinner->setValue(silenceSeconds);
|
|
auto suffix = ki18ncp("Unit of time", " second", " seconds");
|
|
_tabsUi->silenceSecondsSpinner->setSuffix(suffix);
|
|
int silenceCheckBoxWidth = maxSpinBoxWidth(_generalUi->terminalColumnsEntry, suffix);
|
|
_tabsUi->silenceSecondsSpinner->setFixedWidth(silenceCheckBoxWidth);
|
|
|
|
connect(_tabsUi->silenceSecondsSpinner, &QSpinBox::valueChanged, this, &Konsole::EditProfileDialog::silenceSecondsChanged);
|
|
}
|
|
|
|
void EditProfileDialog::terminalColumnsEntryChanged(int value)
|
|
{
|
|
updateTempProfileProperty(Profile::TerminalColumns, value);
|
|
}
|
|
|
|
void EditProfileDialog::terminalRowsEntryChanged(int value)
|
|
{
|
|
updateTempProfileProperty(Profile::TerminalRows, value);
|
|
}
|
|
|
|
void EditProfileDialog::showTerminalSizeHint(bool value)
|
|
{
|
|
updateTempProfileProperty(Profile::ShowTerminalSizeHint, value);
|
|
}
|
|
|
|
void EditProfileDialog::setDimWhenInactive(bool value)
|
|
{
|
|
updateTempProfileProperty(Profile::DimWhenInactive, value);
|
|
}
|
|
|
|
void EditProfileDialog::setDimValue(int value)
|
|
{
|
|
updateTempProfileProperty(Profile::DimValue, value);
|
|
}
|
|
|
|
void EditProfileDialog::setBorderWhenActive(bool value)
|
|
{
|
|
updateTempProfileProperty(Profile::BorderWhenActive, value);
|
|
}
|
|
|
|
void EditProfileDialog::setFocusBorderColor(const QColor &color)
|
|
{
|
|
updateTempProfileProperty(Profile::FocusBorderColor, color);
|
|
}
|
|
|
|
void EditProfileDialog::tabTitleFormatChanged(const QString &format)
|
|
{
|
|
updateTempProfileProperty(Profile::LocalTabTitleFormat, format);
|
|
}
|
|
|
|
void EditProfileDialog::remoteTabTitleFormatChanged(const QString &format)
|
|
{
|
|
updateTempProfileProperty(Profile::RemoteTabTitleFormat, format);
|
|
}
|
|
|
|
void EditProfileDialog::tabColorChanged(const QColor &color)
|
|
{
|
|
updateTempProfileProperty(Profile::TabColor, color);
|
|
}
|
|
|
|
void EditProfileDialog::focusBorderColorChanged(const QColor &color)
|
|
{
|
|
updateTempProfileProperty(Profile::FocusBorderColor, color);
|
|
}
|
|
|
|
void EditProfileDialog::silenceSecondsChanged(int seconds)
|
|
{
|
|
updateTempProfileProperty(Profile::SilenceSeconds, seconds);
|
|
}
|
|
|
|
void EditProfileDialog::selectIcon()
|
|
{
|
|
const QString &icon = KIconDialog::getIcon(KIconLoader::Desktop, KIconLoader::Application, false, 0, false, this);
|
|
if (!icon.isEmpty()) {
|
|
_generalUi->iconSelectButton->setIcon(QIcon::fromTheme(icon));
|
|
updateTempProfileProperty(Profile::Icon, icon);
|
|
}
|
|
}
|
|
|
|
void EditProfileDialog::profileNameChanged(const QString &name)
|
|
{
|
|
updateTempProfileProperty(Profile::Name, name);
|
|
updateTempProfileProperty(Profile::UntranslatedName, name);
|
|
updateCaption(_tempProfile);
|
|
}
|
|
|
|
void EditProfileDialog::startInSameDir(bool sameDir)
|
|
{
|
|
updateTempProfileProperty(Profile::StartInCurrentSessionDir, sameDir);
|
|
}
|
|
|
|
void EditProfileDialog::semanticUpDown(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::SemanticUpDown, enable);
|
|
}
|
|
|
|
void EditProfileDialog::semanticInputClick(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::SemanticInputClick, enable);
|
|
}
|
|
|
|
void EditProfileDialog::initialDirChanged(const QString &dir)
|
|
{
|
|
updateTempProfileProperty(Profile::Directory, dir);
|
|
}
|
|
|
|
void EditProfileDialog::commandChanged(const QString &command)
|
|
{
|
|
ShellCommand shellCommand(command);
|
|
|
|
updateTempProfileProperty(Profile::Command, shellCommand.command());
|
|
updateTempProfileProperty(Profile::Arguments, shellCommand.arguments());
|
|
}
|
|
|
|
void EditProfileDialog::selectInitialDir()
|
|
{
|
|
const QUrl url = QFileDialog::getExistingDirectoryUrl(this, i18n("Select Initial Directory"), QUrl::fromUserInput(_generalUi->initialDirEdit->text()));
|
|
|
|
if (!url.isEmpty()) {
|
|
_generalUi->initialDirEdit->setText(url.path());
|
|
}
|
|
}
|
|
|
|
void EditProfileDialog::setupAppearancePage(const Profile::Ptr &profile)
|
|
{
|
|
auto compositingActiveHelper = [] {
|
|
return WindowSystemInfo::compositingActive();
|
|
};
|
|
auto delegate = new ColorSchemeViewDelegate(compositingActiveHelper, this);
|
|
_appearanceUi->colorSchemeList->setItemDelegate(delegate);
|
|
|
|
_appearanceUi->transparencyWarningWidget->setVisible(false);
|
|
_appearanceUi->transparencyWarningWidget->setWordWrap(true);
|
|
_appearanceUi->transparencyWarningWidget->setCloseButtonVisible(false);
|
|
_appearanceUi->transparencyWarningWidget->setMessageType(KMessageWidget::Warning);
|
|
|
|
_appearanceUi->colorSchemeMessageWidget->setVisible(false);
|
|
_appearanceUi->colorSchemeMessageWidget->setWordWrap(true);
|
|
_appearanceUi->colorSchemeMessageWidget->setCloseButtonVisible(false);
|
|
_appearanceUi->colorSchemeMessageWidget->setMessageType(KMessageWidget::Warning);
|
|
|
|
_appearanceUi->editColorSchemeButton->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
|
|
_appearanceUi->removeColorSchemeButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
|
|
_appearanceUi->newColorSchemeButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
|
|
_appearanceUi->chooseFontButton->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-font")));
|
|
_appearanceUi->resetColorSchemeButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo")));
|
|
|
|
_appearanceUi->editColorSchemeButton->setEnabled(false);
|
|
_appearanceUi->removeColorSchemeButton->setEnabled(false);
|
|
_appearanceUi->resetColorSchemeButton->setEnabled(false);
|
|
|
|
// setup color list
|
|
// select the colorScheme used in the current profile
|
|
updateColorSchemeList(currentColorSchemeName());
|
|
|
|
_appearanceUi->colorSchemeList->setMouseTracking(true);
|
|
_appearanceUi->colorSchemeList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
|
|
|
connect(_appearanceUi->colorSchemeList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Konsole::EditProfileDialog::colorSchemeSelected);
|
|
connect(_appearanceUi->colorSchemeList, &QListView::activated, this, &Konsole::EditProfileDialog::previewColorScheme);
|
|
|
|
updateColorSchemeButtons();
|
|
|
|
connect(_appearanceUi->editColorSchemeButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::editColorScheme);
|
|
connect(_appearanceUi->removeColorSchemeButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::removeColorScheme);
|
|
connect(_appearanceUi->newColorSchemeButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::newColorScheme);
|
|
|
|
connect(_appearanceUi->resetColorSchemeButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::resetColorScheme);
|
|
|
|
connect(_appearanceUi->chooseFontButton, &QAbstractButton::clicked, this, &EditProfileDialog::showFontDialog);
|
|
|
|
connect(_appearanceUi->emojiFontPreview, &QAbstractButton::clicked, this, &EditProfileDialog::showEmojiFontDialog);
|
|
|
|
// setup font preview
|
|
const bool antialias = profile->antiAliasFonts();
|
|
|
|
QFont profileFont = profile->font();
|
|
profileFont.setStyleStrategy(antialias ? QFont::PreferAntialias : QFont::NoAntialias);
|
|
|
|
_appearanceUi->fontPreview->setFont(profileFont);
|
|
_appearanceUi->fontPreview->setText(QStringLiteral("%1 %2pt").arg(profileFont.family()).arg(profileFont.pointSize()));
|
|
|
|
QFont emojiFont = profile->emojiFont();
|
|
_appearanceUi->emojiFontPreview->setFont(profileFont);
|
|
_appearanceUi->emojiFontPreview->setText(QStringLiteral("%1 %2pt").arg(emojiFont.family()).arg(emojiFont.pointSize()));
|
|
|
|
// setup font smoothing
|
|
_appearanceUi->antialiasTextButton->setChecked(antialias);
|
|
connect(_appearanceUi->antialiasTextButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::setAntialiasText);
|
|
|
|
_appearanceUi->boldIntenseButton->setChecked(profile->boldIntense());
|
|
connect(_appearanceUi->boldIntenseButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::setBoldIntense);
|
|
|
|
_appearanceUi->useFontLineCharactersButton->setChecked(profile->useFontLineCharacters());
|
|
connect(_appearanceUi->useFontLineCharactersButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::useFontLineCharacters);
|
|
|
|
_appearanceUi->useFontBrailleCharactersButton->setEnabled(!profile->property<bool>(Profile::UseFontLineCharacters));
|
|
_appearanceUi->useFontBrailleCharactersButton->setChecked(profile->property<bool>(Profile::UseFontBrailleCharacters));
|
|
connect(_appearanceUi->useFontBrailleCharactersButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::useFontBrailleCharacters);
|
|
|
|
_mouseUi->enableMouseWheelZoomButton->setChecked(profile->mouseWheelZoomEnabled());
|
|
connect(_mouseUi->enableMouseWheelZoomButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::toggleMouseWheelZoom);
|
|
|
|
// cursor options
|
|
_appearanceUi->enableBlinkingCursorButton->setChecked(profile->property<bool>(Profile::BlinkingCursorEnabled));
|
|
connect(_appearanceUi->enableBlinkingCursorButton, &QToolButton::toggled, this, &EditProfileDialog::toggleBlinkingCursor);
|
|
_appearanceUi->enableAnimatingCursorButton->setChecked(profile->property<bool>(Profile::AnimatingCursorEnabled));
|
|
connect(_appearanceUi->enableAnimatingCursorButton, &QToolButton::toggled, this, &EditProfileDialog::toggleAnimatingCursor);
|
|
|
|
if (profile->useCustomCursorColor()) {
|
|
_appearanceUi->customCursorColorButton->setChecked(true);
|
|
} else {
|
|
_appearanceUi->autoCursorColorButton->setChecked(true);
|
|
}
|
|
|
|
_appearanceUi->customColorSelectButton->setColor(profile->customCursorColor());
|
|
_appearanceUi->customTextColorSelectButton->setColor(profile->customCursorTextColor());
|
|
|
|
connect(_appearanceUi->customCursorColorButton, &QRadioButton::clicked, this, &Konsole::EditProfileDialog::customCursorColor);
|
|
connect(_appearanceUi->autoCursorColorButton, &QRadioButton::clicked, this, &Konsole::EditProfileDialog::autoCursorColor);
|
|
connect(_appearanceUi->customColorSelectButton, &KColorButton::changed, this, &Konsole::EditProfileDialog::customCursorColorChanged);
|
|
connect(_appearanceUi->customTextColorSelectButton, &KColorButton::changed, this, &Konsole::EditProfileDialog::customCursorTextColorChanged);
|
|
|
|
const ButtonGroupOptions cursorShapeOptions = {
|
|
_appearanceUi->cursorShape, // group
|
|
Profile::CursorShape, // profileProperty
|
|
true, // preview
|
|
{
|
|
// buttons
|
|
{_appearanceUi->cursorShapeBlock, Enum::BlockCursor},
|
|
{_appearanceUi->cursorShapeIBeam, Enum::IBeamCursor},
|
|
{_appearanceUi->cursorShapeUnderline, Enum::UnderlineCursor},
|
|
},
|
|
};
|
|
setupButtonGroup(cursorShapeOptions, profile);
|
|
|
|
_appearanceUi->marginsSpinner->setValue(profile->terminalMargin());
|
|
connect(_appearanceUi->marginsSpinner, &QSpinBox::valueChanged, this, &Konsole::EditProfileDialog::terminalMarginChanged);
|
|
|
|
_appearanceUi->lineSpacingSpinner->setValue(profile->lineSpacing());
|
|
connect(_appearanceUi->lineSpacingSpinner, &QSpinBox::valueChanged, this, &Konsole::EditProfileDialog::lineSpacingChanged);
|
|
|
|
_appearanceUi->alignToCenterButton->setChecked(profile->terminalCenter());
|
|
connect(_appearanceUi->alignToCenterButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::setTerminalCenter);
|
|
|
|
_appearanceUi->showTerminalSizeHintButton->setChecked(profile->showTerminalSizeHint());
|
|
connect(_appearanceUi->showTerminalSizeHintButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::showTerminalSizeHint);
|
|
|
|
_appearanceUi->dimWhenInactiveCheckbox->setChecked(profile->dimWhenInactive());
|
|
connect(_appearanceUi->dimWhenInactiveCheckbox, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::setDimWhenInactive);
|
|
|
|
_appearanceUi->dimValue->setValue(profile->dimValue());
|
|
_appearanceUi->dimValue->setEnabled(profile->dimWhenInactive());
|
|
_appearanceUi->dimLabel->setEnabled(profile->dimWhenInactive());
|
|
connect(_appearanceUi->dimValue, &QSlider::valueChanged, this, &Konsole::EditProfileDialog::setDimValue);
|
|
|
|
_appearanceUi->borderWhenActiveCheckbox->setChecked(profile->borderWhenActive());
|
|
connect(_appearanceUi->borderWhenActiveCheckbox, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::setBorderWhenActive);
|
|
|
|
_appearanceUi->focusBorderColor->setColor(profile->focusBorderColor());
|
|
_appearanceUi->focusBorderColor->setEnabled(profile->borderWhenActive());
|
|
_appearanceUi->borderLabel->setEnabled(profile->borderWhenActive());
|
|
connect(_appearanceUi->focusBorderColor, &KColorButton::changed, this, &Konsole::EditProfileDialog::focusBorderColorChanged);
|
|
|
|
_appearanceUi->invertSelectionColorsCheckbox->setChecked(profile->property<bool>(Profile::InvertSelectionColors));
|
|
connect(_appearanceUi->invertSelectionColorsCheckbox, &QCheckBox::toggled, this, [this](bool checked) {
|
|
updateTempProfileProperty(Profile::InvertSelectionColors, checked);
|
|
});
|
|
|
|
_appearanceUi->displayVerticalLine->setChecked(profile->verticalLine());
|
|
connect(_appearanceUi->displayVerticalLine, &QCheckBox::toggled, this, &EditProfileDialog::setVerticalLine);
|
|
|
|
_appearanceUi->displayVerticalLineAtColumn->setValue(profile->verticalLineAtChar());
|
|
connect(_appearanceUi->displayVerticalLineAtColumn, &QSpinBox::valueChanged, this, &EditProfileDialog::setVerticalLineColumn);
|
|
|
|
auto *getNewButton = new KNSWidgets::Button(this);
|
|
connect(getNewButton, &KNSWidgets::Button::dialogFinished, this, &Konsole::EditProfileDialog::gotNewColorSchemes);
|
|
|
|
getNewButton->setText(i18n("Get New..."));
|
|
getNewButton->setConfigFile(QStringLiteral(":/konsole/konsole.knsrc"));
|
|
_appearanceUi->colorSchemesBtnLayout->addWidget(getNewButton);
|
|
|
|
_appearanceUi->enableBidiRenderingButton->setChecked(profile->bidiRenderingEnabled());
|
|
connect(_appearanceUi->enableBidiRenderingButton, &QPushButton::toggled, this, &EditProfileDialog::togglebidiRendering);
|
|
|
|
_appearanceUi->enableBidiTableDirOverrideButton->setChecked(profile->property<bool>(Profile::BidiTableDirOverride));
|
|
connect(_appearanceUi->enableBidiTableDirOverrideButton, &QPushButton::toggled, this, &EditProfileDialog::togglebidiTableDirOverride);
|
|
_appearanceUi->enableBidiTableDirOverrideButton->setEnabled(profile->bidiRenderingEnabled());
|
|
|
|
_appearanceUi->bidiLineLTR->setChecked(profile->property<bool>(Profile::BidiLineLTR));
|
|
connect(_appearanceUi->bidiLineLTR, &QPushButton::toggled, this, &EditProfileDialog::togglebidiLineLTR);
|
|
_appearanceUi->bidiLineLTR->setEnabled(profile->bidiRenderingEnabled());
|
|
|
|
_appearanceUi->wordMode->setChecked(profile->property<bool>(Profile::WordMode));
|
|
connect(_appearanceUi->wordMode, &QPushButton::toggled, this, &EditProfileDialog::toggleWordMode);
|
|
_appearanceUi->wordModeAttr->setEnabled(profile->property<bool>(Profile::WordMode));
|
|
_appearanceUi->wordModeAscii->setEnabled(profile->property<bool>(Profile::WordMode));
|
|
_appearanceUi->wordModeBrahmic->setEnabled(profile->property<bool>(Profile::WordMode));
|
|
|
|
_appearanceUi->wordModeAttr->setChecked(profile->property<bool>(Profile::WordModeAttr));
|
|
connect(_appearanceUi->wordModeAttr, &QPushButton::toggled, this, &EditProfileDialog::toggleWordModeAttr);
|
|
|
|
_appearanceUi->wordModeAscii->setChecked(profile->property<bool>(Profile::WordModeAscii));
|
|
connect(_appearanceUi->wordModeAscii, &QPushButton::toggled, this, &EditProfileDialog::toggleWordModeAscii);
|
|
|
|
_appearanceUi->wordModeBrahmic->setChecked(profile->property<bool>(Profile::WordModeBrahmic));
|
|
connect(_appearanceUi->wordModeBrahmic, &QPushButton::toggled, this, &EditProfileDialog::toggleWordModeBrahmic);
|
|
|
|
_appearanceUi->ignoreWcWidth->setChecked(profile->property<bool>(Profile::IgnoreWcWidth));
|
|
connect(_appearanceUi->ignoreWcWidth, &QPushButton::toggled, this, &EditProfileDialog::toggleIgnoreWcWidth);
|
|
}
|
|
|
|
void EditProfileDialog::setAntialiasText(bool enable)
|
|
{
|
|
preview(Profile::AntiAliasFonts, enable);
|
|
updateTempProfileProperty(Profile::AntiAliasFonts, enable);
|
|
|
|
const QFont font = _profile->font();
|
|
updateFontPreview(font);
|
|
}
|
|
|
|
void EditProfileDialog::toggleAllowLinkEscapeSequence(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::AllowEscapedLinks, enable);
|
|
}
|
|
|
|
void EditProfileDialog::linkEscapeSequenceTextsChanged()
|
|
{
|
|
updateTempProfileProperty(Profile::EscapedLinksSchema, _mouseUi->linkEscapeSequenceTexts->text());
|
|
}
|
|
void EditProfileDialog::setVerticalLine(bool value)
|
|
{
|
|
updateTempProfileProperty(Profile::VerticalLine, value);
|
|
}
|
|
|
|
void EditProfileDialog::setVerticalLineColumn(int value)
|
|
{
|
|
updateTempProfileProperty(Profile::VerticalLineAtChar, value);
|
|
}
|
|
|
|
void EditProfileDialog::setBoldIntense(bool enable)
|
|
{
|
|
preview(Profile::BoldIntense, enable);
|
|
updateTempProfileProperty(Profile::BoldIntense, enable);
|
|
}
|
|
|
|
void EditProfileDialog::useFontLineCharacters(bool enable)
|
|
{
|
|
_appearanceUi->useFontBrailleCharactersButton->setEnabled(!enable);
|
|
preview(Profile::UseFontLineCharacters, enable);
|
|
updateTempProfileProperty(Profile::UseFontLineCharacters, enable);
|
|
}
|
|
|
|
void EditProfileDialog::useFontBrailleCharacters(bool enable)
|
|
{
|
|
preview(Profile::UseFontBrailleCharacters, enable);
|
|
updateTempProfileProperty(Profile::UseFontBrailleCharacters, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleBlinkingCursor(bool enable)
|
|
{
|
|
preview(Profile::BlinkingCursorEnabled, enable);
|
|
updateTempProfileProperty(Profile::BlinkingCursorEnabled, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleAnimatingCursor(bool enable)
|
|
{
|
|
preview(Profile::AnimatingCursorEnabled, enable);
|
|
updateTempProfileProperty(Profile::AnimatingCursorEnabled, enable);
|
|
}
|
|
|
|
void EditProfileDialog::setCursorShape(int index)
|
|
{
|
|
preview(Profile::CursorShape, index);
|
|
updateTempProfileProperty(Profile::CursorShape, index);
|
|
}
|
|
|
|
void EditProfileDialog::autoCursorColor()
|
|
{
|
|
preview(Profile::UseCustomCursorColor, false);
|
|
updateTempProfileProperty(Profile::UseCustomCursorColor, false);
|
|
}
|
|
|
|
void EditProfileDialog::customCursorColor()
|
|
{
|
|
preview(Profile::UseCustomCursorColor, true);
|
|
updateTempProfileProperty(Profile::UseCustomCursorColor, true);
|
|
}
|
|
|
|
void EditProfileDialog::customCursorColorChanged(const QColor &color)
|
|
{
|
|
preview(Profile::CustomCursorColor, color);
|
|
updateTempProfileProperty(Profile::CustomCursorColor, color);
|
|
|
|
// ensure that custom cursor colors are enabled
|
|
_appearanceUi->customCursorColorButton->click();
|
|
}
|
|
|
|
void EditProfileDialog::customCursorTextColorChanged(const QColor &color)
|
|
{
|
|
preview(Profile::CustomCursorTextColor, color);
|
|
updateTempProfileProperty(Profile::CustomCursorTextColor, color);
|
|
|
|
// ensure that custom cursor colors are enabled
|
|
_appearanceUi->customCursorColorButton->click();
|
|
}
|
|
|
|
void EditProfileDialog::terminalMarginChanged(int margin)
|
|
{
|
|
preview(Profile::TerminalMargin, margin);
|
|
updateTempProfileProperty(Profile::TerminalMargin, margin);
|
|
}
|
|
|
|
void EditProfileDialog::lineSpacingChanged(int spacing)
|
|
{
|
|
preview(Profile::LineSpacing, spacing);
|
|
updateTempProfileProperty(Profile::LineSpacing, spacing);
|
|
}
|
|
|
|
void EditProfileDialog::setTerminalCenter(bool enable)
|
|
{
|
|
preview(Profile::TerminalCenter, enable);
|
|
updateTempProfileProperty(Profile::TerminalCenter, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleMouseWheelZoom(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::MouseWheelZoomEnabled, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleAlternateScrolling(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::AlternateScrolling, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleAllowColorFilter(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::ColorFilterEnabled, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleAllowMouseTracking(bool allow)
|
|
{
|
|
updateTempProfileProperty(Profile::AllowMouseTracking, allow);
|
|
}
|
|
|
|
void EditProfileDialog::updateColorSchemeList(const QString &selectedColorSchemeName)
|
|
{
|
|
if (_appearanceUi->colorSchemeList->model() == nullptr) {
|
|
_appearanceUi->colorSchemeList->setModel(new QStandardItemModel(this));
|
|
}
|
|
|
|
std::shared_ptr<const ColorScheme> selectedColorScheme = ColorSchemeManager::instance()->findColorScheme(selectedColorSchemeName);
|
|
|
|
auto *model = qobject_cast<QStandardItemModel *>(_appearanceUi->colorSchemeList->model());
|
|
|
|
Q_ASSERT(model);
|
|
|
|
model->clear();
|
|
|
|
QStandardItem *selectedItem = nullptr;
|
|
|
|
const QList<std::shared_ptr<const ColorScheme>> schemeList = ColorSchemeManager::instance()->allColorSchemes();
|
|
|
|
for (const std::shared_ptr<const ColorScheme> &scheme : schemeList) {
|
|
QStandardItem *item = new QStandardItem(scheme->description());
|
|
item->setData(QVariant::fromValue(scheme), Qt::UserRole + 1);
|
|
item->setData(QVariant::fromValue(_profile->font()), Qt::UserRole + 2);
|
|
item->setFlags(item->flags());
|
|
|
|
// if selectedColorSchemeName is not empty then select that scheme
|
|
// after saving the changes in the colorScheme editor
|
|
if (selectedColorScheme == scheme) {
|
|
selectedItem = item;
|
|
}
|
|
|
|
model->appendRow(item);
|
|
}
|
|
|
|
model->sort(0);
|
|
|
|
if (selectedItem != nullptr) {
|
|
_appearanceUi->colorSchemeList->updateGeometry();
|
|
_appearanceUi->colorSchemeList->selectionModel()->setCurrentIndex(selectedItem->index(), QItemSelectionModel::Select);
|
|
|
|
// update transparency warning label
|
|
updateTransparencyWarning();
|
|
}
|
|
}
|
|
|
|
void EditProfileDialog::updateKeyBindingsList(const QString &selectKeyBindingsName)
|
|
{
|
|
if (_keyboardUi->keyBindingList->model() == nullptr) {
|
|
_keyboardUi->keyBindingList->setModel(new QStandardItemModel(this));
|
|
}
|
|
|
|
auto *model = qobject_cast<QStandardItemModel *>(_keyboardUi->keyBindingList->model());
|
|
|
|
Q_ASSERT(model);
|
|
|
|
model->clear();
|
|
|
|
QStandardItem *selectedItem = nullptr;
|
|
|
|
const QStringList &translatorNames = _keyManager->allTranslators();
|
|
for (const QString &translatorName : translatorNames) {
|
|
const KeyboardTranslator *translator = _keyManager->findTranslator(translatorName);
|
|
if (translator == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
QStandardItem *item = new QStandardItem(translator->description());
|
|
item->setEditable(false);
|
|
item->setData(QVariant::fromValue(translator), Qt::UserRole + 1);
|
|
item->setData(QVariant::fromValue(_keyManager->findTranslatorPath(translatorName)), Qt::ToolTipRole);
|
|
item->setData(QVariant::fromValue(_profile->font()), Qt::UserRole + 2);
|
|
item->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-keyboard")));
|
|
|
|
if (selectKeyBindingsName == translatorName) {
|
|
selectedItem = item;
|
|
}
|
|
|
|
model->appendRow(item);
|
|
}
|
|
|
|
model->sort(0);
|
|
|
|
if (selectedItem != nullptr) {
|
|
_keyboardUi->keyBindingList->selectionModel()->setCurrentIndex(selectedItem->index(), QItemSelectionModel::Select);
|
|
}
|
|
}
|
|
|
|
QSize EditProfileDialog::sizeHint() const
|
|
{
|
|
QFontMetrics fm(font());
|
|
const int ch = fm.boundingRect(QLatin1Char('0')).width();
|
|
|
|
// By default minimum size is used. Increase it to make text inputs
|
|
// on "tabs" page wider and to add some whitespace on right side
|
|
// of other pages. The window will not be wider than 2/3 of
|
|
// the screen width (unless necessary to fit everything)
|
|
return QDialog::sizeHint() + QSize(10 * ch, 0);
|
|
}
|
|
|
|
void EditProfileDialog::unpreviewAll()
|
|
{
|
|
// undo any preview changes
|
|
if (!_previewedProperties.empty()) {
|
|
ProfileManager::instance()->changeProfile(_profile, _previewedProperties, false);
|
|
}
|
|
}
|
|
|
|
void EditProfileDialog::unpreview(Profile::Property prop)
|
|
{
|
|
auto node = _previewedProperties.extract(prop);
|
|
if (!node) {
|
|
return;
|
|
}
|
|
|
|
Profile::PropertyMap map;
|
|
map.insert(std::move(node));
|
|
ProfileManager::instance()->changeProfile(_profile, map, false);
|
|
}
|
|
|
|
void EditProfileDialog::preview(Profile::Property prop, const QVariant &value)
|
|
{
|
|
const Profile::Ptr original = _profile;
|
|
|
|
// skip previews for profile groups if the profiles in the group
|
|
// have conflicting original values for the property
|
|
//
|
|
// TODO - Save the original values for each profile and use to unpreview properties
|
|
ProfileGroup::Ptr group = original->asGroup();
|
|
if (group && group->profiles().count() > 1 && original->property<QVariant>(prop).isNull()) {
|
|
return;
|
|
}
|
|
|
|
_previewedProperties.insert({prop, original->property<QVariant>(prop)});
|
|
|
|
// temporary change to color scheme
|
|
ProfileManager::instance()->changeProfile(_profile, {{prop, value}}, false);
|
|
}
|
|
|
|
void EditProfileDialog::previewColorScheme(const QModelIndex &index)
|
|
{
|
|
const QString &name = index.data(Qt::UserRole + 1).value<std::shared_ptr<const ColorScheme>>()->name();
|
|
preview(Profile::ColorScheme, name);
|
|
}
|
|
|
|
void EditProfileDialog::showFontDialog()
|
|
{
|
|
if (_fontDialog == nullptr) {
|
|
_fontDialog = new FontDialog(this);
|
|
_fontDialog->setModal(true);
|
|
connect(_fontDialog, &FontDialog::fontChanged, this, [this](const QFont &font) {
|
|
preview(Profile::Font, font);
|
|
updateFontPreview(font);
|
|
});
|
|
connect(_fontDialog, &FontDialog::accepted, this, [this]() {
|
|
const QFont font = _fontDialog->font();
|
|
preview(Profile::Font, font);
|
|
updateTempProfileProperty(Profile::Font, font);
|
|
updateFontPreview(font);
|
|
});
|
|
connect(_fontDialog, &FontDialog::rejected, this, [this]() {
|
|
unpreview(Profile::Font);
|
|
updateFontPreview(_profile->font());
|
|
});
|
|
}
|
|
_fontDialog->setFont(_profile->font());
|
|
_fontDialog->show();
|
|
}
|
|
|
|
void EditProfileDialog::showEmojiFontDialog()
|
|
{
|
|
if (_emojiFontDialog == nullptr) {
|
|
_emojiFontDialog = new FontDialog(this, true, _profile->emojiFont());
|
|
_emojiFontDialog->setModal(true);
|
|
connect(_emojiFontDialog, &FontDialog::fontChanged, this, [this](const QFont &font) {
|
|
preview(Profile::EmojiFont, font);
|
|
updateEmojiFontPreview(font);
|
|
});
|
|
connect(_emojiFontDialog, &FontDialog::accepted, this, [this]() {
|
|
const QFont font = _emojiFontDialog->font();
|
|
preview(Profile::EmojiFont, font);
|
|
updateTempProfileProperty(Profile::EmojiFont, font);
|
|
updateEmojiFontPreview(font);
|
|
});
|
|
connect(_emojiFontDialog, &FontDialog::rejected, this, [this]() {
|
|
unpreview(Profile::EmojiFont);
|
|
updateEmojiFontPreview(_profile->emojiFont());
|
|
});
|
|
}
|
|
_emojiFontDialog->show();
|
|
}
|
|
|
|
void EditProfileDialog::updateFontPreview(QFont font)
|
|
{
|
|
bool aa = _profile->antiAliasFonts();
|
|
font.setStyleStrategy(aa ? QFont::PreferAntialias : QFont::NoAntialias);
|
|
|
|
_appearanceUi->fontPreview->setFont(font);
|
|
_appearanceUi->fontPreview->setText(QStringLiteral("%1 %2pt").arg(font.family()).arg(font.pointSize()));
|
|
}
|
|
|
|
void EditProfileDialog::updateEmojiFontPreview(QFont font)
|
|
{
|
|
bool aa = _profile->antiAliasFonts();
|
|
font.setStyleStrategy(aa ? QFont::PreferAntialias : QFont::NoAntialias);
|
|
QFont emojiFont = _profile->emojiFont();
|
|
_appearanceUi->emojiFontPreview->setFont(_profile->font());
|
|
_appearanceUi->emojiFontPreview->setText(QStringLiteral("%1 %2pt").arg(emojiFont.family()).arg(emojiFont.pointSize()));
|
|
}
|
|
|
|
void EditProfileDialog::removeColorScheme()
|
|
{
|
|
const QModelIndexList selected = _appearanceUi->colorSchemeList->selectionModel()->selectedIndexes();
|
|
if (selected.isEmpty()) {
|
|
return;
|
|
}
|
|
const QString &name = selected.first().data(Qt::UserRole + 1).value<std::shared_ptr<const ColorScheme>>()->name();
|
|
Q_ASSERT(!name.isEmpty());
|
|
if (ColorSchemeManager::instance()->deleteColorScheme(name)) {
|
|
_appearanceUi->colorSchemeList->model()->removeRow(selected.first().row());
|
|
}
|
|
}
|
|
|
|
void EditProfileDialog::gotNewColorSchemes(const QList<KNSCore::Entry> &changedEntries)
|
|
{
|
|
int failures = 0;
|
|
for (auto &entry : std::as_const(changedEntries)) {
|
|
switch (entry.status()) {
|
|
case KNSCore::Entry::Installed: {
|
|
const auto installedFiles = entry.installedFiles();
|
|
for (const QString &file : installedFiles) {
|
|
if (ColorSchemeManager::instance()->loadColorScheme(file)) {
|
|
continue;
|
|
}
|
|
qWarning() << "Failed to load file" << file;
|
|
++failures;
|
|
}
|
|
if (failures == entry.installedFiles().size()) {
|
|
_appearanceUi->colorSchemeMessageWidget->setText(xi18nc("@info", "Scheme <resource>%1</resource> failed to load.", entry.name()));
|
|
_appearanceUi->colorSchemeMessageWidget->animatedShow();
|
|
QTimer::singleShot(8000, _appearanceUi->colorSchemeMessageWidget, &KMessageWidget::animatedHide);
|
|
}
|
|
break;
|
|
}
|
|
case KNSCore::Entry::Deleted: {
|
|
const auto uninstalledFiles = entry.uninstalledFiles();
|
|
for (const auto &file : uninstalledFiles) {
|
|
if (ColorSchemeManager::instance()->unloadColorScheme(file)) {
|
|
continue;
|
|
}
|
|
qWarning() << "Failed to unload file" << file;
|
|
// If unloading fails we do not care. If the scheme failed here
|
|
// it either wasn't loaded or was invalid to begin with.
|
|
}
|
|
break;
|
|
}
|
|
case KNSCore::Entry::Invalid:
|
|
case KNSCore::Entry::Installing:
|
|
case KNSCore::Entry::Downloadable:
|
|
case KNSCore::Entry::Updateable:
|
|
case KNSCore::Entry::Updating:
|
|
// Not interesting.
|
|
break;
|
|
}
|
|
}
|
|
updateColorSchemeList(currentColorSchemeName());
|
|
}
|
|
|
|
void EditProfileDialog::resetColorScheme()
|
|
{
|
|
QModelIndexList selected = _appearanceUi->colorSchemeList->selectionModel()->selectedIndexes();
|
|
|
|
if (!selected.isEmpty()) {
|
|
const QString &name = selected.first().data(Qt::UserRole + 1).value<std::shared_ptr<const ColorScheme>>()->name();
|
|
|
|
ColorSchemeManager::instance()->deleteColorScheme(name);
|
|
|
|
// select the colorScheme used in the current profile
|
|
updateColorSchemeList(currentColorSchemeName());
|
|
}
|
|
}
|
|
|
|
void EditProfileDialog::showColorSchemeEditor(bool isNewScheme)
|
|
{
|
|
// Finding selected ColorScheme
|
|
QModelIndexList selected = _appearanceUi->colorSchemeList->selectionModel()->selectedIndexes();
|
|
QAbstractItemModel *model = _appearanceUi->colorSchemeList->model();
|
|
std::shared_ptr<const ColorScheme> colors;
|
|
if (!selected.isEmpty()) {
|
|
colors = model->data(selected.first(), Qt::UserRole + 1).value<std::shared_ptr<const ColorScheme>>();
|
|
} else {
|
|
colors = ColorSchemeManager::instance()->defaultColorScheme();
|
|
}
|
|
|
|
Q_ASSERT(colors);
|
|
|
|
// Setting up ColorSchemeEditor ui
|
|
// close any running ColorSchemeEditor
|
|
if (_colorDialog != nullptr) {
|
|
closeColorSchemeEditor();
|
|
}
|
|
_colorDialog = new ColorSchemeEditor(WindowSystemInfo::compositingActive(), this);
|
|
|
|
connect(_colorDialog, &Konsole::ColorSchemeEditor::colorSchemeSaveRequested, this, &Konsole::EditProfileDialog::saveColorScheme);
|
|
_colorDialog->setup(colors, isNewScheme);
|
|
|
|
_colorDialog->show();
|
|
}
|
|
|
|
void EditProfileDialog::closeColorSchemeEditor()
|
|
{
|
|
if (_colorDialog != nullptr) {
|
|
_colorDialog->close();
|
|
delete _colorDialog;
|
|
}
|
|
}
|
|
|
|
void EditProfileDialog::newColorScheme()
|
|
{
|
|
showColorSchemeEditor(true);
|
|
}
|
|
|
|
void EditProfileDialog::editColorScheme()
|
|
{
|
|
showColorSchemeEditor(false);
|
|
}
|
|
|
|
void EditProfileDialog::saveColorScheme(const ColorScheme &scheme, bool isNewScheme)
|
|
{
|
|
std::shared_ptr<ColorScheme> newScheme = std::make_shared<ColorScheme>(scheme);
|
|
|
|
// if this is a new color scheme, pick a name based on the description
|
|
if (isNewScheme) {
|
|
newScheme->setName(newScheme->description());
|
|
}
|
|
|
|
ColorSchemeManager::instance()->addColorScheme(newScheme);
|
|
|
|
const QString &selectedColorSchemeName = newScheme->name();
|
|
|
|
// select the edited or the new colorScheme after saving the changes
|
|
updateColorSchemeList(selectedColorSchemeName);
|
|
|
|
preview(Profile::ColorScheme, newScheme->name());
|
|
}
|
|
|
|
void EditProfileDialog::colorSchemeSelected()
|
|
{
|
|
QModelIndexList selected = _appearanceUi->colorSchemeList->selectionModel()->selectedIndexes();
|
|
|
|
if (!selected.isEmpty()) {
|
|
QAbstractItemModel *model = _appearanceUi->colorSchemeList->model();
|
|
std::shared_ptr<const ColorScheme> colors = model->data(selected.first(), Qt::UserRole + 1).value<std::shared_ptr<const ColorScheme>>();
|
|
if (colors != nullptr) {
|
|
updateTempProfileProperty(Profile::ColorScheme, colors->name());
|
|
previewColorScheme(selected.first());
|
|
|
|
updateTransparencyWarning();
|
|
}
|
|
}
|
|
|
|
updateColorSchemeButtons();
|
|
}
|
|
|
|
void EditProfileDialog::updateColorSchemeButtons()
|
|
{
|
|
enableIfNonEmptySelection(_appearanceUi->editColorSchemeButton, _appearanceUi->colorSchemeList->selectionModel());
|
|
|
|
QModelIndexList selected = _appearanceUi->colorSchemeList->selectionModel()->selectedIndexes();
|
|
|
|
if (!selected.isEmpty()) {
|
|
const QString &name = selected.first().data(Qt::UserRole + 1).value<std::shared_ptr<const ColorScheme>>()->name();
|
|
|
|
bool isResettable = ColorSchemeManager::instance()->canResetColorScheme(name);
|
|
_appearanceUi->resetColorSchemeButton->setEnabled(isResettable);
|
|
|
|
bool isDeletable = ColorSchemeManager::instance()->isColorSchemeDeletable(name);
|
|
// if a colorScheme can be restored then it can't be deleted
|
|
_appearanceUi->removeColorSchemeButton->setEnabled(isDeletable && !isResettable);
|
|
} else {
|
|
_appearanceUi->removeColorSchemeButton->setEnabled(false);
|
|
_appearanceUi->resetColorSchemeButton->setEnabled(false);
|
|
}
|
|
}
|
|
|
|
void EditProfileDialog::updateKeyBindingsButtons()
|
|
{
|
|
QModelIndexList selected = _keyboardUi->keyBindingList->selectionModel()->selectedIndexes();
|
|
|
|
if (!selected.isEmpty()) {
|
|
_keyboardUi->editKeyBindingsButton->setEnabled(true);
|
|
|
|
const QString &name = selected.first().data(Qt::UserRole + 1).value<const KeyboardTranslator *>()->name();
|
|
|
|
bool isResettable = _keyManager->isTranslatorResettable(name);
|
|
_keyboardUi->resetKeyBindingsButton->setEnabled(isResettable);
|
|
|
|
bool isDeletable = _keyManager->isTranslatorDeletable(name);
|
|
|
|
// if a key bindings scheme can be reset then it can't be deleted
|
|
_keyboardUi->removeKeyBindingsButton->setEnabled(isDeletable && !isResettable);
|
|
}
|
|
}
|
|
|
|
void EditProfileDialog::enableIfNonEmptySelection(QWidget *widget, QItemSelectionModel *selectionModel)
|
|
{
|
|
widget->setEnabled(selectionModel->hasSelection());
|
|
}
|
|
|
|
void EditProfileDialog::updateTransparencyWarning()
|
|
{
|
|
// zero or one indexes can be selected
|
|
const QModelIndexList selected = _appearanceUi->colorSchemeList->selectionModel()->selectedIndexes();
|
|
for (const QModelIndex &index : selected) {
|
|
bool needTransparency = index.data(Qt::UserRole + 1).value<std::shared_ptr<const ColorScheme>>()->opacity() < 1.0;
|
|
|
|
if (!needTransparency) {
|
|
_appearanceUi->transparencyWarningWidget->setHidden(true);
|
|
} else if (!WindowSystemInfo::compositingActive()) {
|
|
_appearanceUi->transparencyWarningWidget->setText(
|
|
i18n("This color scheme uses a transparent background"
|
|
" which does not appear to be supported on your"
|
|
" desktop"));
|
|
_appearanceUi->transparencyWarningWidget->setHidden(false);
|
|
} else if (!WindowSystemInfo::HAVE_TRANSPARENCY) {
|
|
_appearanceUi->transparencyWarningWidget->setText(
|
|
i18n("Konsole was started before desktop effects were enabled."
|
|
" You need to restart Konsole to see transparent background."));
|
|
_appearanceUi->transparencyWarningWidget->setHidden(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EditProfileDialog::resetTempProfile()
|
|
{
|
|
_tempProfile->clear();
|
|
_tempProfile->setHidden(true);
|
|
}
|
|
|
|
void EditProfileDialog::updateTempProfileProperty(Profile::Property property, const QVariant &value)
|
|
{
|
|
_tempProfile->setProperty(property, value);
|
|
updateButtonApply();
|
|
}
|
|
|
|
void EditProfileDialog::updateButtonApply()
|
|
{
|
|
auto filterFunc = [this](const auto &keyValue) {
|
|
const auto &[property, value] = keyValue;
|
|
// for previewed property
|
|
auto it = _previewedProperties.find(property);
|
|
if (it != _previewedProperties.cend()) {
|
|
if (value != it->second) {
|
|
return true;
|
|
}
|
|
// for not-previewed property
|
|
//
|
|
// for the Profile::KeyBindings property, if it's set in the _tempProfile
|
|
// then the user opened the edit key bindings dialog and clicked
|
|
// OK, and could have add/removed a key bindings rule
|
|
} else if (property == Profile::KeyBindings //
|
|
|| value != _profile->property<QVariant>(property)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
const Profile::PropertyMap &map = _tempProfile->properties();
|
|
bool userModified = std::any_of(map.cbegin(), map.cend(), filterFunc);
|
|
|
|
if (_generalUi->setAsDefaultButton->isChecked() != _isDefault) {
|
|
userModified = true;
|
|
}
|
|
|
|
_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(userModified);
|
|
}
|
|
|
|
void EditProfileDialog::setupKeyboardPage(const Profile::Ptr & /* profile */)
|
|
{
|
|
// setup translator list
|
|
updateKeyBindingsList(_profile->keyBindings());
|
|
|
|
connect(_keyboardUi->keyBindingList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Konsole::EditProfileDialog::keyBindingSelected);
|
|
connect(_keyboardUi->newKeyBindingsButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::newKeyBinding);
|
|
|
|
_keyboardUi->editKeyBindingsButton->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
|
|
_keyboardUi->removeKeyBindingsButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
|
|
_keyboardUi->newKeyBindingsButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
|
|
_keyboardUi->resetKeyBindingsButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo")));
|
|
|
|
_keyboardUi->editKeyBindingsButton->setEnabled(false);
|
|
_keyboardUi->removeKeyBindingsButton->setEnabled(false);
|
|
_keyboardUi->resetKeyBindingsButton->setEnabled(false);
|
|
|
|
updateKeyBindingsButtons();
|
|
|
|
connect(_keyboardUi->editKeyBindingsButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::editKeyBinding);
|
|
connect(_keyboardUi->removeKeyBindingsButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::removeKeyBinding);
|
|
connect(_keyboardUi->resetKeyBindingsButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::resetKeyBindings);
|
|
}
|
|
|
|
void EditProfileDialog::keyBindingSelected()
|
|
{
|
|
QModelIndexList selected = _keyboardUi->keyBindingList->selectionModel()->selectedIndexes();
|
|
|
|
if (!selected.isEmpty()) {
|
|
QAbstractItemModel *model = _keyboardUi->keyBindingList->model();
|
|
const auto *translator = model->data(selected.first(), Qt::UserRole + 1).value<const KeyboardTranslator *>();
|
|
if (translator != nullptr) {
|
|
updateTempProfileProperty(Profile::KeyBindings, translator->name());
|
|
}
|
|
}
|
|
|
|
updateKeyBindingsButtons();
|
|
}
|
|
|
|
void EditProfileDialog::removeKeyBinding()
|
|
{
|
|
QModelIndexList selected = _keyboardUi->keyBindingList->selectionModel()->selectedIndexes();
|
|
|
|
if (!selected.isEmpty()) {
|
|
const QString &name = selected.first().data(Qt::UserRole + 1).value<const KeyboardTranslator *>()->name();
|
|
if (KeyboardTranslatorManager::instance()->deleteTranslator(name)) {
|
|
_keyboardUi->keyBindingList->model()->removeRow(selected.first().row());
|
|
}
|
|
}
|
|
}
|
|
|
|
void EditProfileDialog::showKeyBindingEditor(bool isNewTranslator)
|
|
{
|
|
QModelIndexList selected = _keyboardUi->keyBindingList->selectionModel()->selectedIndexes();
|
|
QAbstractItemModel *model = _keyboardUi->keyBindingList->model();
|
|
|
|
const KeyboardTranslator *translator = nullptr;
|
|
if (!selected.isEmpty()) {
|
|
translator = model->data(selected.first(), Qt::UserRole + 1).value<const KeyboardTranslator *>();
|
|
} else {
|
|
translator = _keyManager->defaultTranslator();
|
|
}
|
|
|
|
auto *editor = new KeyBindingEditor(this);
|
|
editor->setAttribute(Qt::WA_DeleteOnClose);
|
|
editor->setModal(true);
|
|
|
|
if (translator != nullptr) {
|
|
editor->setup(translator, _profile->keyBindings(), isNewTranslator);
|
|
}
|
|
|
|
connect(editor, &Konsole::KeyBindingEditor::updateKeyBindingsListRequest, this, &Konsole::EditProfileDialog::updateKeyBindingsList);
|
|
connect(editor, &Konsole::KeyBindingEditor::updateTempProfileKeyBindingsRequest, this, &Konsole::EditProfileDialog::updateTempProfileProperty);
|
|
|
|
editor->show();
|
|
}
|
|
|
|
void EditProfileDialog::newKeyBinding()
|
|
{
|
|
showKeyBindingEditor(true);
|
|
}
|
|
|
|
void EditProfileDialog::editKeyBinding()
|
|
{
|
|
showKeyBindingEditor(false);
|
|
}
|
|
|
|
void EditProfileDialog::resetKeyBindings()
|
|
{
|
|
QModelIndexList selected = _keyboardUi->keyBindingList->selectionModel()->selectedIndexes();
|
|
|
|
if (!selected.isEmpty()) {
|
|
const QString &name = selected.first().data(Qt::UserRole + 1).value<const KeyboardTranslator *>()->name();
|
|
|
|
_keyManager->deleteTranslator(name);
|
|
// find and load the translator
|
|
_keyManager->findTranslator(name);
|
|
|
|
updateKeyBindingsList(name);
|
|
}
|
|
}
|
|
|
|
void EditProfileDialog::setupButtonGroup(const ButtonGroupOptions &options, const Profile::Ptr &profile)
|
|
{
|
|
auto currentValue = profile->property<int>(options.profileProperty);
|
|
|
|
for (auto option : options.buttons) {
|
|
options.group->setId(option.button, option.value);
|
|
}
|
|
|
|
Q_ASSERT(options.buttons.count() > 0);
|
|
auto *activeButton = options.group->button(currentValue);
|
|
if (activeButton == nullptr) {
|
|
activeButton = options.buttons[0].button;
|
|
}
|
|
activeButton->setChecked(true);
|
|
connect(options.group, &QButtonGroup::idClicked, this, [this, options](int value) {
|
|
if (options.preview) {
|
|
preview(options.profileProperty, value);
|
|
}
|
|
updateTempProfileProperty(options.profileProperty, value);
|
|
});
|
|
}
|
|
|
|
void EditProfileDialog::setupScrollingPage(const Profile::Ptr &profile)
|
|
{
|
|
// setup scrollbar radio
|
|
const ButtonGroupOptions scrollBarPositionOptions = {
|
|
_scrollingUi->scrollBarPosition, // group
|
|
Profile::ScrollBarPosition, // profileProperty
|
|
false, // preview
|
|
{
|
|
// buttons
|
|
{_scrollingUi->scrollBarRightButton, Enum::ScrollBarRight},
|
|
{_scrollingUi->scrollBarLeftButton, Enum::ScrollBarLeft},
|
|
{_scrollingUi->scrollBarHiddenButton, Enum::ScrollBarHidden},
|
|
},
|
|
};
|
|
setupButtonGroup(scrollBarPositionOptions, profile);
|
|
|
|
// setup scrollback type radio
|
|
auto scrollBackType = profile->property<int>(Profile::HistoryMode);
|
|
_scrollingUi->historySizeWidget->setMode(Enum::HistoryModeEnum(scrollBackType));
|
|
connect(_scrollingUi->historySizeWidget, &Konsole::HistorySizeWidget::historyModeChanged, this, &Konsole::EditProfileDialog::historyModeChanged);
|
|
|
|
// setup scrollback line count spinner
|
|
const int historySize = profile->historySize();
|
|
_scrollingUi->historySizeWidget->setLineCount(historySize);
|
|
|
|
// setup scrollpageamount type radio
|
|
auto scrollFullPage = profile->property<int>(Profile::ScrollFullPage);
|
|
|
|
_scrollingUi->scrollHalfPage->setChecked(Enum::ScrollPageHalf == scrollFullPage);
|
|
connect(_scrollingUi->scrollHalfPage, &QPushButton::clicked, this, &EditProfileDialog::scrollHalfPage);
|
|
|
|
_scrollingUi->scrollFullPage->setChecked(Enum::ScrollPageFull == scrollFullPage);
|
|
connect(_scrollingUi->scrollFullPage, &QPushButton::clicked, this, &EditProfileDialog::scrollFullPage);
|
|
|
|
_scrollingUi->highlightScrolledLinesButton->setChecked(profile->property<bool>(Profile::HighlightScrolledLines));
|
|
connect(_scrollingUi->highlightScrolledLinesButton, &QPushButton::clicked, this, &EditProfileDialog::toggleHighlightScrolledLines);
|
|
|
|
_scrollingUi->reflowLinesButton->setChecked(profile->property<bool>(Profile::ReflowLines));
|
|
connect(_scrollingUi->reflowLinesButton, &QPushButton::clicked, this, &EditProfileDialog::toggleReflowLines);
|
|
|
|
// setup marker color button
|
|
_scrollingUi->markerColorButton->setColor(profile->property<QColor>(Profile::MarkerColor));
|
|
connect(_scrollingUi->markerColorButton, &KColorButton::changed, this, &Konsole::EditProfileDialog::toggleScrollbarMarkerColor);
|
|
|
|
// setup marker size widget
|
|
_scrollingUi->markerSizeWidget->setValue(profile->property<double>(Profile::MarkerSize));
|
|
connect(_scrollingUi->markerSizeWidget, &QDoubleSpinBox::valueChanged, this, &Konsole::EditProfileDialog::toggleScrollbarMarkerSize);
|
|
|
|
// setup search highlighter color button
|
|
_scrollingUi->searchHighligheterColorButton->setColor(profile->property<QColor>(Profile::SearchLineColor));
|
|
connect(_scrollingUi->searchHighligheterColorButton, &KColorButton::changed, this, &Konsole::EditProfileDialog::toggleScrollbarSearchLineColor);
|
|
|
|
// setup search highlighter opacity widget
|
|
_scrollingUi->labelSearchHighlighterOpacitySlider->setValue(profile->property<int>(Profile::SearchLineOpacity));
|
|
connect(_scrollingUi->labelSearchHighlighterOpacitySlider, &QSlider::valueChanged, this, &Konsole::EditProfileDialog::toggleScrollbarSearchLineOpacity);
|
|
// signals and slots
|
|
connect(_scrollingUi->historySizeWidget, &Konsole::HistorySizeWidget::historySizeChanged, this, &Konsole::EditProfileDialog::historySizeChanged);
|
|
}
|
|
|
|
void EditProfileDialog::historySizeChanged(int lineCount)
|
|
{
|
|
updateTempProfileProperty(Profile::HistorySize, lineCount);
|
|
}
|
|
|
|
void EditProfileDialog::historyModeChanged(Enum::HistoryModeEnum mode)
|
|
{
|
|
updateTempProfileProperty(Profile::HistoryMode, mode);
|
|
}
|
|
|
|
void EditProfileDialog::scrollFullPage()
|
|
{
|
|
updateTempProfileProperty(Profile::ScrollFullPage, Enum::ScrollPageFull);
|
|
}
|
|
|
|
void EditProfileDialog::scrollHalfPage()
|
|
{
|
|
updateTempProfileProperty(Profile::ScrollFullPage, Enum::ScrollPageHalf);
|
|
}
|
|
|
|
void EditProfileDialog::toggleHighlightScrolledLines(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::HighlightScrolledLines, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleReflowLines(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::ReflowLines, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleScrollbarMarkerColor(QColor color)
|
|
{
|
|
updateTempProfileProperty(Profile::MarkerColor, color);
|
|
}
|
|
|
|
void EditProfileDialog::toggleScrollbarMarkerSize(double pSize)
|
|
{
|
|
updateTempProfileProperty(Profile::MarkerSize, pSize);
|
|
}
|
|
|
|
void EditProfileDialog::toggleScrollbarSearchLineColor(QColor color) {
|
|
updateTempProfileProperty(Profile::SearchLineColor, color);
|
|
}
|
|
|
|
void EditProfileDialog::toggleScrollbarSearchLineOpacity(int opacity) {
|
|
updateTempProfileProperty(Profile::SearchLineOpacity, opacity);
|
|
}
|
|
|
|
void EditProfileDialog::setupMousePage(const Profile::Ptr &profile)
|
|
{
|
|
_mouseUi->underlineLinksButton->setChecked(profile->property<bool>(Profile::UnderlineLinksEnabled));
|
|
connect(_mouseUi->underlineLinksButton, &QPushButton::toggled, this, &EditProfileDialog::toggleUnderlineLinks);
|
|
_mouseUi->underlineFilesButton->setChecked(profile->property<bool>(Profile::UnderlineFilesEnabled));
|
|
connect(_mouseUi->underlineFilesButton, &QPushButton::toggled, this, &EditProfileDialog::toggleUnderlineFiles);
|
|
_mouseUi->ctrlRequiredForDragButton->setChecked(profile->property<bool>(Profile::CtrlRequiredForDrag));
|
|
connect(_mouseUi->ctrlRequiredForDragButton, &QPushButton::toggled, this, &EditProfileDialog::toggleCtrlRequiredForDrag);
|
|
_mouseUi->copyTextAsHTMLButton->setChecked(profile->property<bool>(Profile::CopyTextAsHTML));
|
|
connect(_mouseUi->copyTextAsHTMLButton, &QPushButton::toggled, this, &EditProfileDialog::toggleCopyTextAsHTML);
|
|
_mouseUi->copyTextToClipboardButton->setChecked(profile->property<bool>(Profile::AutoCopySelectedText));
|
|
connect(_mouseUi->copyTextToClipboardButton, &QPushButton::toggled, this, &EditProfileDialog::toggleCopyTextToClipboard);
|
|
_mouseUi->trimLeadingSpacesButton->setChecked(profile->property<bool>(Profile::TrimLeadingSpacesInSelectedText));
|
|
connect(_mouseUi->trimLeadingSpacesButton, &QPushButton::toggled, this, &EditProfileDialog::toggleTrimLeadingSpacesInSelectedText);
|
|
_mouseUi->trimTrailingSpacesButton->setChecked(profile->property<bool>(Profile::TrimTrailingSpacesInSelectedText));
|
|
connect(_mouseUi->trimTrailingSpacesButton, &QPushButton::toggled, this, &EditProfileDialog::toggleTrimTrailingSpacesInSelectedText);
|
|
_mouseUi->openLinksByDirectClickButton->setChecked(profile->property<bool>(Profile::OpenLinksByDirectClickEnabled));
|
|
connect(_mouseUi->openLinksByDirectClickButton, &QPushButton::toggled, this, &EditProfileDialog::toggleOpenLinksByDirectClick);
|
|
_mouseUi->dropUrlsAsText->setChecked(profile->property<bool>(Profile::DropUrlsAsText));
|
|
connect(_mouseUi->dropUrlsAsText, &QPushButton::toggled, this, &EditProfileDialog::toggleDropUrlsAsText);
|
|
_mouseUi->enableAlternateScrollingButton->setChecked(profile->property<bool>(Profile::AlternateScrolling));
|
|
connect(_mouseUi->enableAlternateScrollingButton, &QPushButton::toggled, this, &EditProfileDialog::toggleAlternateScrolling);
|
|
_mouseUi->allowColorFilters->setChecked(profile->property<bool>(Profile::ColorFilterEnabled));
|
|
connect(_mouseUi->allowColorFilters, &QPushButton::toggled, this, &EditProfileDialog::toggleAllowColorFilter);
|
|
_mouseUi->allowMouseTrackingButton->setChecked(profile->property<bool>(Profile::AllowMouseTracking));
|
|
connect(_mouseUi->allowMouseTrackingButton, &QPushButton::toggled, this, &EditProfileDialog::toggleAllowMouseTracking);
|
|
|
|
// setup middle click paste mode
|
|
const auto middleClickPasteMode = profile->property<int>(Profile::MiddleClickPasteMode);
|
|
_mouseUi->pasteFromX11SelectionButton->setChecked(Enum::PasteFromX11Selection == middleClickPasteMode);
|
|
connect(_mouseUi->pasteFromX11SelectionButton, &QPushButton::clicked, this, &EditProfileDialog::pasteFromX11Selection);
|
|
_mouseUi->pasteFromClipboardButton->setChecked(Enum::PasteFromClipboard == middleClickPasteMode);
|
|
connect(_mouseUi->pasteFromClipboardButton, &QPushButton::clicked, this, &EditProfileDialog::pasteFromClipboard);
|
|
|
|
_mouseUi->textEditorCustomBtn->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
|
|
|
|
// interaction options
|
|
_mouseUi->wordCharacterEdit->setText(profile->wordCharacters());
|
|
|
|
connect(_mouseUi->wordCharacterEdit, &QLineEdit::textChanged, this, &Konsole::EditProfileDialog::wordCharactersChanged);
|
|
|
|
const ButtonGroupOptions tripleClickModeOptions = {
|
|
_mouseUi->tripleClickMode, // group
|
|
Profile::TripleClickMode, // profileProperty
|
|
false, // preview
|
|
{
|
|
// buttons
|
|
{_mouseUi->tripleClickSelectsTheWholeLine, Enum::SelectWholeLine},
|
|
{_mouseUi->tripleClickSelectsFromMousePosition, Enum::SelectForwardsFromCursor},
|
|
},
|
|
};
|
|
setupButtonGroup(tripleClickModeOptions, profile);
|
|
|
|
_mouseUi->openLinksByDirectClickButton->setEnabled(_mouseUi->underlineLinksButton->isChecked() || _mouseUi->underlineFilesButton->isChecked());
|
|
|
|
_mouseUi->enableMouseWheelZoomButton->setChecked(profile->mouseWheelZoomEnabled());
|
|
connect(_mouseUi->enableMouseWheelZoomButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::toggleMouseWheelZoom);
|
|
|
|
_mouseUi->allowLinkEscapeSequenceButton->setChecked(profile->allowEscapedLinks());
|
|
connect(_mouseUi->allowLinkEscapeSequenceButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::toggleAllowLinkEscapeSequence);
|
|
|
|
_mouseUi->linkEscapeSequenceTexts->setEnabled(profile->allowEscapedLinks());
|
|
_mouseUi->linkEscapeSequenceTexts->setText(profile->escapedLinksSchema().join(QLatin1Char(';')));
|
|
connect(_mouseUi->linkEscapeSequenceTexts, &QLineEdit::textChanged, this, &Konsole::EditProfileDialog::linkEscapeSequenceTextsChanged);
|
|
|
|
setTextEditorCombo(profile);
|
|
}
|
|
|
|
void EditProfileDialog::setTextEditorCombo(const Profile::Ptr &profile)
|
|
{
|
|
static const Enum::TextEditorCmd editorsList[] =
|
|
{Enum::Kate, Enum::KWrite, Enum::KDevelop, Enum::QtCreator, Enum::Gedit, Enum::gVim, Enum::CustomTextEditor, Enum::Default};
|
|
|
|
auto *editorCombo = _mouseUi->textEditorCombo;
|
|
|
|
QStandardItemModel *model = static_cast<QStandardItemModel *>(editorCombo->model());
|
|
Q_ASSERT(model);
|
|
|
|
for (auto editor : editorsList) {
|
|
QString exec;
|
|
QString displayName;
|
|
QIcon icon;
|
|
switch (editor) {
|
|
case Enum::Kate:
|
|
exec = QStringLiteral("kate");
|
|
displayName = QStringLiteral("Kate");
|
|
icon = QIcon::fromTheme(exec);
|
|
break;
|
|
case Enum::KWrite:
|
|
exec = QStringLiteral("kwrite");
|
|
displayName = QStringLiteral("KWrite");
|
|
icon = QIcon::fromTheme(exec);
|
|
break;
|
|
case Enum::KDevelop:
|
|
exec = QStringLiteral("kdevelop");
|
|
displayName = QStringLiteral("KDevelop");
|
|
icon = QIcon::fromTheme(exec);
|
|
break;
|
|
case Enum::QtCreator:
|
|
exec = QStringLiteral("qtcreator");
|
|
displayName = QStringLiteral("Qt Creator");
|
|
icon = QIcon::fromTheme(exec);
|
|
break;
|
|
case Enum::Gedit:
|
|
exec = QStringLiteral("gedit");
|
|
displayName = QStringLiteral("Gedit");
|
|
QIcon::fromTheme(QStringLiteral("org.gnome.gedit"));
|
|
break;
|
|
case Enum::gVim:
|
|
exec = QStringLiteral("gvim");
|
|
displayName = QStringLiteral("gVim");
|
|
icon = QIcon::fromTheme(exec);
|
|
break;
|
|
case Enum::CustomTextEditor:
|
|
displayName = QStringLiteral("Custom");
|
|
icon = QIcon::fromTheme(QStringLiteral("system-run"));
|
|
break;
|
|
case Enum::Default:
|
|
displayName = i18n("Default for file type");
|
|
icon = QIcon::fromTheme(QStringLiteral("preferences-desktop-default-applications"));
|
|
default:
|
|
break;
|
|
}
|
|
|
|
editorCombo->addItem(icon, displayName);
|
|
|
|
// For "CustomTextEditor" we don't check if the binary exists
|
|
const bool isAvailable = editor == Enum::CustomTextEditor || editor == Enum::Default || !QStandardPaths::findExecutable(exec).isEmpty();
|
|
// Make un-available editors look disabled in the combobox
|
|
model->item(static_cast<int>(editor))->setEnabled(isAvailable);
|
|
}
|
|
|
|
const auto currentEditor = profile->property<int>(Profile::TextEditorCmd);
|
|
editorCombo->setCurrentIndex(currentEditor);
|
|
|
|
connect(editorCombo, &QComboBox::currentIndexChanged, this, [this](const int index) {
|
|
updateTempProfileProperty(Profile::TextEditorCmd, index);
|
|
_mouseUi->textEditorCustomBtn->setEnabled(index == Enum::CustomTextEditor);
|
|
});
|
|
|
|
_mouseUi->textEditorCustomBtn->setEnabled(currentEditor == Enum::CustomTextEditor);
|
|
connect(_mouseUi->textEditorCustomBtn, &QAbstractButton::clicked, this, [this, profile]() {
|
|
auto *dlg = new QInputDialog(static_cast<QWidget *>(this));
|
|
dlg->setLabelText(
|
|
i18n("The format is e.g. 'editorExec PATH:LINE:COLUMN'\n\n"
|
|
"PATH will be replaced by the path to the text file\n"
|
|
"LINE will be replaced by the line number\n"
|
|
"COLUMN (optional) will be replaced by the column number\n"
|
|
"Note: you will need to replace 'PATH:LINE:COLUMN' by the actual\n"
|
|
"syntax the editor you want to use supports; e.g.:\n"
|
|
"gedit +LINE:COLUMN PATH\n\n"
|
|
"If PATH or LINE aren't present in the command, this setting\n"
|
|
"will be ignored and the file will be opened by the default text\n"
|
|
"editor."));
|
|
const QString cmd = profile->customTextEditorCmd();
|
|
dlg->setTextValue(cmd);
|
|
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
|
dlg->setWindowTitle(i18n("Text Editor Custom Command"));
|
|
|
|
QFontMetrics fm(font());
|
|
const int width = qMin(fm.averageCharWidth() * cmd.size(), this->width());
|
|
dlg->resize(width, dlg->height());
|
|
|
|
connect(dlg, &QDialog::accepted, this, [this, dlg]() {
|
|
updateTempProfileProperty(Profile::TextEditorCmdCustom, dlg->textValue());
|
|
});
|
|
|
|
dlg->show();
|
|
});
|
|
}
|
|
|
|
void EditProfileDialog::setupAdvancedPage(const Profile::Ptr &profile)
|
|
{
|
|
_advancedUi->enableBlinkingTextButton->setChecked(profile->property<bool>(Profile::BlinkingTextEnabled));
|
|
connect(_advancedUi->enableBlinkingTextButton, &QPushButton::toggled, this, &EditProfileDialog::toggleBlinkingText);
|
|
_advancedUi->enableFlowControlButton->setChecked(profile->property<bool>(Profile::FlowControlEnabled));
|
|
connect(_advancedUi->enableFlowControlButton, &QPushButton::toggled, this, &EditProfileDialog::toggleFlowControl);
|
|
_appearanceUi->enableBlinkingCursorButton->setChecked(profile->property<bool>(Profile::BlinkingCursorEnabled));
|
|
connect(_appearanceUi->enableBlinkingCursorButton, &QPushButton::toggled, this, &EditProfileDialog::toggleBlinkingCursor);
|
|
_advancedUi->enableReverseUrlHints->setChecked(profile->property<bool>(Profile::ReverseUrlHints));
|
|
connect(_advancedUi->enableReverseUrlHints, &QPushButton::toggled, this, &EditProfileDialog::toggleReverseUrlHints);
|
|
|
|
// Setup the URL hints modifier checkboxes
|
|
{
|
|
auto modifiers = profile->property<int>(Profile::UrlHintsModifiers);
|
|
_advancedUi->urlHintsModifierShift->setChecked((modifiers & Qt::ShiftModifier) != 0U);
|
|
_advancedUi->urlHintsModifierCtrl->setChecked((modifiers & Qt::ControlModifier) != 0U);
|
|
_advancedUi->urlHintsModifierAlt->setChecked((modifiers & Qt::AltModifier) != 0U);
|
|
_advancedUi->urlHintsModifierMeta->setChecked((modifiers & Qt::MetaModifier) != 0U);
|
|
connect(_advancedUi->urlHintsModifierShift, &QCheckBox::toggled, this, &EditProfileDialog::updateUrlHintsModifier);
|
|
connect(_advancedUi->urlHintsModifierCtrl, &QCheckBox::toggled, this, &EditProfileDialog::updateUrlHintsModifier);
|
|
connect(_advancedUi->urlHintsModifierAlt, &QCheckBox::toggled, this, &EditProfileDialog::updateUrlHintsModifier);
|
|
connect(_advancedUi->urlHintsModifierMeta, &QCheckBox::toggled, this, &EditProfileDialog::updateUrlHintsModifier);
|
|
}
|
|
|
|
_advancedUi->autoSaveIntervalWidget->setValue(profile->property<int>(Profile::AutoSaveInterval));
|
|
connect(_advancedUi->autoSaveIntervalWidget, &QSpinBox::valueChanged, this, &EditProfileDialog::setAutoSaveInterval);
|
|
|
|
// encoding options
|
|
auto codecAction = new KCodecAction(this);
|
|
codecAction->setCurrentCodec(profile->defaultEncoding());
|
|
_advancedUi->selectEncodingButton->setMenu(codecAction->menu());
|
|
|
|
connect(codecAction, &KCodecAction::codecNameTriggered, this, [this](const QByteArray &codecName) {
|
|
setDefaultCodec(codecName);
|
|
});
|
|
|
|
connect(codecAction, &KCodecAction::defaultItemTriggered, this, [this] {
|
|
setDefaultCodec(QStringConverter::nameForEncoding(QStringConverter::System));
|
|
});
|
|
|
|
_advancedUi->selectEncodingButton->setText(profile->defaultEncoding());
|
|
|
|
_advancedUi->peekPrimaryWidget->setKeySequence(profile->peekPrimaryKeySequence());
|
|
connect(_advancedUi->peekPrimaryWidget, &QKeySequenceEdit::editingFinished, this, &EditProfileDialog::peekPrimaryKeySequenceChanged);
|
|
|
|
const ButtonGroupOptions lineNums = {
|
|
_advancedUi->lineNums, // group
|
|
Profile::LineNumbers, // profileProperty
|
|
false, // preview
|
|
{
|
|
// buttons
|
|
{_advancedUi->lineNumsNever, Enum::HintsNever},
|
|
{_advancedUi->lineNumsURL, Enum::HintsURL},
|
|
{_advancedUi->lineNumsAlways, Enum::HintsAlways},
|
|
},
|
|
};
|
|
setupButtonGroup(lineNums, profile);
|
|
}
|
|
|
|
int EditProfileDialog::maxSpinBoxWidth(const KPluralHandlingSpinBox *spinBox, const KLocalizedString &suffix)
|
|
{
|
|
static const int cursorWidth = 2;
|
|
|
|
const QFontMetrics fm(spinBox->fontMetrics());
|
|
const QString plural = suffix.subs(2).toString();
|
|
const QString singular = suffix.subs(1).toString();
|
|
const QString min = QString::number(spinBox->minimum());
|
|
const QString max = QString::number(spinBox->maximum());
|
|
const int pluralWidth = fm.boundingRect(plural).width();
|
|
const int singularWidth = fm.boundingRect(singular).width();
|
|
const int minWidth = fm.boundingRect(min).width();
|
|
const int maxWidth = fm.boundingRect(max).width();
|
|
const int width = qMax(pluralWidth, singularWidth) + qMax(minWidth, maxWidth) + cursorWidth;
|
|
|
|
// Based on QAbstractSpinBox::initStyleOption() from Qt
|
|
QStyleOptionSpinBox opt;
|
|
opt.initFrom(spinBox);
|
|
opt.activeSubControls = QStyle::SC_None;
|
|
opt.buttonSymbols = spinBox->buttonSymbols();
|
|
// Assume all spinboxes have buttons
|
|
opt.subControls = QStyle::SC_SpinBoxFrame | QStyle::SC_SpinBoxEditField | QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown;
|
|
opt.frame = spinBox->hasFrame();
|
|
|
|
const QSize hint(width, spinBox->sizeHint().height());
|
|
const QSize spinBoxSize = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, spinBox);
|
|
return spinBoxSize.width();
|
|
}
|
|
|
|
void EditProfileDialog::setDefaultCodec(const QByteArray &codec)
|
|
{
|
|
QString name = QString::fromLocal8Bit(codec);
|
|
|
|
updateTempProfileProperty(Profile::DefaultEncoding, name);
|
|
_advancedUi->selectEncodingButton->setText(name);
|
|
}
|
|
|
|
void EditProfileDialog::wordCharactersChanged(const QString &text)
|
|
{
|
|
updateTempProfileProperty(Profile::WordCharacters, text);
|
|
}
|
|
|
|
void EditProfileDialog::togglebidiRendering(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::BidiRenderingEnabled, enable);
|
|
_appearanceUi->enableBidiTableDirOverrideButton->setEnabled(enable);
|
|
_appearanceUi->bidiLineLTR->setEnabled(enable);
|
|
}
|
|
|
|
void EditProfileDialog::togglebidiTableDirOverride(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::BidiTableDirOverride, enable);
|
|
}
|
|
|
|
void EditProfileDialog::togglebidiLineLTR(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::BidiLineLTR, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleUnderlineLinks(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::UnderlineLinksEnabled, enable);
|
|
|
|
bool enableClick = _mouseUi->underlineFilesButton->isChecked() || enable;
|
|
_mouseUi->openLinksByDirectClickButton->setEnabled(enableClick);
|
|
}
|
|
|
|
void EditProfileDialog::toggleUnderlineFiles(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::UnderlineFilesEnabled, enable);
|
|
|
|
bool enableClick = _mouseUi->underlineLinksButton->isChecked() || enable;
|
|
_mouseUi->openLinksByDirectClickButton->setEnabled(enableClick);
|
|
}
|
|
|
|
void EditProfileDialog::textEditorCmdEditLineChanged(const QString &text)
|
|
{
|
|
updateTempProfileProperty(Profile::TextEditorCmd, text);
|
|
}
|
|
|
|
void EditProfileDialog::toggleCtrlRequiredForDrag(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::CtrlRequiredForDrag, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleDropUrlsAsText(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::DropUrlsAsText, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleOpenLinksByDirectClick(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::OpenLinksByDirectClickEnabled, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleCopyTextAsHTML(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::CopyTextAsHTML, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleCopyTextToClipboard(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::AutoCopySelectedText, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleTrimLeadingSpacesInSelectedText(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::TrimLeadingSpacesInSelectedText, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleTrimTrailingSpacesInSelectedText(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::TrimTrailingSpacesInSelectedText, enable);
|
|
}
|
|
|
|
void EditProfileDialog::pasteFromX11Selection()
|
|
{
|
|
updateTempProfileProperty(Profile::MiddleClickPasteMode, Enum::PasteFromX11Selection);
|
|
}
|
|
|
|
void EditProfileDialog::pasteFromClipboard()
|
|
{
|
|
updateTempProfileProperty(Profile::MiddleClickPasteMode, Enum::PasteFromClipboard);
|
|
}
|
|
|
|
void EditProfileDialog::TripleClickModeChanged(int newValue)
|
|
{
|
|
updateTempProfileProperty(Profile::TripleClickMode, newValue);
|
|
}
|
|
|
|
void EditProfileDialog::updateUrlHintsModifier(bool)
|
|
{
|
|
Qt::KeyboardModifiers modifiers;
|
|
if (_advancedUi->urlHintsModifierShift->isChecked()) {
|
|
modifiers |= Qt::ShiftModifier;
|
|
}
|
|
if (_advancedUi->urlHintsModifierCtrl->isChecked()) {
|
|
modifiers |= Qt::ControlModifier;
|
|
}
|
|
if (_advancedUi->urlHintsModifierAlt->isChecked()) {
|
|
modifiers |= Qt::AltModifier;
|
|
}
|
|
if (_advancedUi->urlHintsModifierMeta->isChecked()) {
|
|
modifiers |= Qt::MetaModifier;
|
|
}
|
|
updateTempProfileProperty(Profile::UrlHintsModifiers, int(modifiers));
|
|
}
|
|
|
|
void EditProfileDialog::toggleReverseUrlHints(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::ReverseUrlHints, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleBlinkingText(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::BlinkingTextEnabled, enable);
|
|
}
|
|
|
|
void EditProfileDialog::toggleFlowControl(bool enable)
|
|
{
|
|
updateTempProfileProperty(Profile::FlowControlEnabled, enable);
|
|
}
|
|
|
|
void EditProfileDialog::setAutoSaveInterval(int newVal)
|
|
{
|
|
updateTempProfileProperty(Profile::AutoSaveInterval, newVal);
|
|
}
|
|
|
|
void EditProfileDialog::peekPrimaryKeySequenceChanged()
|
|
{
|
|
updateTempProfileProperty(Profile::PeekPrimaryKeySequence, _advancedUi->peekPrimaryWidget->keySequence().toString());
|
|
}
|
|
|
|
void EditProfileDialog::toggleWordMode(bool mode)
|
|
{
|
|
updateTempProfileProperty(Profile::WordMode, mode);
|
|
_appearanceUi->wordModeAttr->setEnabled(mode);
|
|
_appearanceUi->wordModeAscii->setEnabled(mode);
|
|
_appearanceUi->wordModeBrahmic->setEnabled(mode);
|
|
}
|
|
|
|
void EditProfileDialog::toggleWordModeAttr(bool mode)
|
|
{
|
|
updateTempProfileProperty(Profile::WordModeAttr, mode);
|
|
}
|
|
|
|
void EditProfileDialog::toggleWordModeAscii(bool mode)
|
|
{
|
|
updateTempProfileProperty(Profile::WordModeAscii, mode);
|
|
}
|
|
|
|
void EditProfileDialog::toggleWordModeBrahmic(bool mode)
|
|
{
|
|
updateTempProfileProperty(Profile::WordModeBrahmic, mode);
|
|
}
|
|
|
|
void EditProfileDialog::toggleIgnoreWcWidth(bool ignore)
|
|
{
|
|
updateTempProfileProperty(Profile::IgnoreWcWidth, ignore);
|
|
}
|
|
|
|
#include "moc_EditProfileDialog.cpp"
|