You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1892 lines
73 KiB
1892 lines
73 KiB
/* |
|
Copyright 2007-2008 by Robert Knight <robertknight@gmail.com> |
|
Copyright 2018 by Harald Sitter <sitter@kde.org> |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation; either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program; if not, write to the Free Software |
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
02110-1301 USA. |
|
*/ |
|
|
|
// Own |
|
#include "EditProfileDialog.h" |
|
|
|
// Qt |
|
#include <QDialog> |
|
#include <QDialogButtonBox> |
|
#include <QFileDialog> |
|
#include <QInputDialog> |
|
#include <QIcon> |
|
#include <QPainter> |
|
#include <QPushButton> |
|
#include <QStandardItem> |
|
#include <QTextCodec> |
|
#include <QTimer> |
|
#include <QUrl> |
|
#include <QStandardPaths> |
|
#include <QRegularExpressionValidator> |
|
|
|
// KDE |
|
#include <KCodecAction> |
|
#include <KIconDialog> |
|
#include <KLocalizedString> |
|
#include <KMessageBox> |
|
#include <KNSCore/DownloadManager> |
|
#include <KWindowSystem> |
|
|
|
// Konsole |
|
#include "ui_EditProfileGeneralPage.h" |
|
#include "ui_EditProfileTabsPage.h" |
|
#include "ui_EditProfileAppearancePage.h" |
|
#include "ui_EditProfileScrollingPage.h" |
|
#include "ui_EditProfileKeyboardPage.h" |
|
#include "ui_EditProfileMousePage.h" |
|
#include "ui_EditProfileAdvancedPage.h" |
|
|
|
#include "colorscheme/ColorSchemeManager.h" |
|
|
|
#include "keyboardtranslator/KeyboardTranslator.h" |
|
#include "keyboardtranslator/KeyboardTranslatorManager.h" |
|
|
|
#include "KeyBindingEditor.h" |
|
#include "profile/ProfileManager.h" |
|
#include "ShellCommand.h" |
|
#include "WindowSystemInfo.h" |
|
#include "FontDialog.h" |
|
|
|
using namespace Konsole; |
|
|
|
EditProfileDialog::EditProfileDialog(QWidget *parent) |
|
: KPageDialog(parent) |
|
, _generalUi(nullptr) |
|
, _tabsUi(nullptr) |
|
, _appearanceUi(nullptr) |
|
, _scrollingUi(nullptr) |
|
, _keyboardUi(nullptr) |
|
, _mouseUi(nullptr) |
|
, _advancedUi(nullptr) |
|
, _tempProfile(nullptr) |
|
, _profile(nullptr) |
|
, _previewedProperties(QHash<int, QVariant>()) |
|
, _delayedPreviewProperties(QHash<int, QVariant>()) |
|
, _delayedPreviewTimer(new QTimer(this)) |
|
, _colorDialog(nullptr) |
|
, _buttonBox(nullptr) |
|
, _fontDialog(nullptr) |
|
{ |
|
setWindowTitle(i18n("Edit Profile")); |
|
setFaceType(KPageDialog::List); |
|
|
|
_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply); |
|
setButtonBox(_buttonBox); |
|
|
|
QPushButton *okButton = _buttonBox->button(QDialogButtonBox::Ok); |
|
okButton->setDefault(true); |
|
connect(_buttonBox, &QDialogButtonBox::accepted, this, &Konsole::EditProfileDialog::accept); |
|
connect(_buttonBox, &QDialogButtonBox::rejected, this, &Konsole::EditProfileDialog::reject); |
|
|
|
// disable the apply button , since no modification has been made |
|
_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); |
|
|
|
connect(_buttonBox->button(QDialogButtonBox::Apply), |
|
&QPushButton::clicked, this, |
|
&Konsole::EditProfileDialog::apply); |
|
|
|
connect(_delayedPreviewTimer, &QTimer::timeout, this, |
|
&Konsole::EditProfileDialog::delayedPreviewActivate); |
|
|
|
// 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); |
|
auto *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("tab-duplicate"), |
|
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("transform-move-vertical"), |
|
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("configure"), |
|
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); |
|
|
|
createTempProfile(); |
|
} |
|
|
|
EditProfileDialog::~EditProfileDialog() |
|
{ |
|
delete _generalUi; |
|
delete _tabsUi; |
|
delete _appearanceUi; |
|
delete _scrollingUi; |
|
delete _keyboardUi; |
|
delete _mouseUi; |
|
delete _advancedUi; |
|
} |
|
|
|
void EditProfileDialog::save() |
|
{ |
|
if (_tempProfile->isEmpty()) { |
|
return; |
|
} |
|
|
|
ProfileManager::instance()->changeProfile(_profile, _tempProfile->setProperties()); |
|
|
|
// ensure that these settings are not undone by a call |
|
// to unpreview() |
|
QHashIterator<Profile::Property, QVariant> iter(_tempProfile->setProperties()); |
|
while (iter.hasNext()) { |
|
iter.next(); |
|
_previewedProperties.remove(iter.key()); |
|
} |
|
|
|
createTempProfile(); |
|
|
|
_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); |
|
} |
|
|
|
void EditProfileDialog::reject() |
|
{ |
|
unpreviewAll(); |
|
QDialog::reject(); |
|
} |
|
|
|
void EditProfileDialog::accept() |
|
{ |
|
// if the Apply button is disabled then no settings were changed |
|
// or the changes have already been saved by apply() |
|
if (_buttonBox->button(QDialogButtonBox::Apply)->isEnabled()) { |
|
if (!isValidProfileName()) { |
|
return; |
|
} |
|
save(); |
|
} |
|
|
|
unpreviewAll(); |
|
QDialog::accept(); |
|
} |
|
|
|
void EditProfileDialog::apply() |
|
{ |
|
if (!isValidProfileName()) { |
|
return; |
|
} |
|
save(); |
|
} |
|
|
|
bool EditProfileDialog::isValidProfileName() |
|
{ |
|
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()) { |
|
if (!_tempProfile->isPropertySet(Profile::Name) |
|
|| (_tempProfile->name() == _profile->name())) { |
|
KMessageBox::sorry(this, |
|
i18n("<p>Konsole does not have permission to save this profile to:<br /> \"%1\"</p>" |
|
"<p>To be able to save settings you can either change the permissions " |
|
"of the profile configuration file or change the profile name to save " |
|
"the settings to a new profile.</p>", _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())) { |
|
KMessageBox::sorry(this, |
|
i18n("<p>Each profile must have a name before it can be saved " |
|
"into disk.</p>")); |
|
// revert the name in the dialog |
|
_generalUi->profileNameEdit->setText(_profile->name()); |
|
selectProfileName(); |
|
return false; |
|
} else if (!_tempProfile->name().isEmpty() && otherExistingProfileNames.contains(_tempProfile->name())) { |
|
KMessageBox::sorry(this, |
|
i18n("<p>A profile with this name already exists.</p>")); |
|
// revert the name in the dialog |
|
_generalUi->profileNameEdit->setText(_profile->name()); |
|
selectProfileName(); |
|
return false; |
|
} else { |
|
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)); |
|
} else { |
|
setWindowTitle(i18n("Edit Profile \"%1\"", profile->name())); |
|
} |
|
} |
|
|
|
void EditProfileDialog::setProfile(const Konsole::Profile::Ptr &profile) |
|
{ |
|
Q_ASSERT(profile); |
|
|
|
_profile = profile; |
|
|
|
// 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()); |
|
|
|
if (_tempProfile) { |
|
createTempProfile(); |
|
} |
|
} |
|
|
|
const Profile::Ptr EditProfileDialog::lookupProfile() const |
|
{ |
|
return _profile; |
|
} |
|
|
|
const QString EditProfileDialog::currentColorSchemeName() const |
|
{ |
|
const QString ¤tColorSchemeName = lookupProfile()->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 = lookupProfile(); |
|
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) |
|
{ |
|
// 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->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); |
|
|
|
// 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->terminalColumnsEntry, |
|
QOverload<int>::of(&QSpinBox::valueChanged), this, |
|
&Konsole::EditProfileDialog::terminalColumnsEntryChanged); |
|
connect(_generalUi->terminalRowsEntry, |
|
QOverload<int>::of(&QSpinBox::valueChanged), this, |
|
&Konsole::EditProfileDialog::terminalRowsEntryChanged); |
|
} |
|
|
|
void EditProfileDialog::showEnvironmentEditor() |
|
{ |
|
bool ok; |
|
const Profile::Ptr profile = lookupProfile(); |
|
|
|
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, |
|
QOverload<int>::of(&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::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::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::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 delegate = new ColorSchemeViewDelegate(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->setEnabled(false); |
|
_appearanceUi->removeColorSchemeButton->setEnabled(false); |
|
_appearanceUi->resetColorSchemeButton->setEnabled(false); |
|
|
|
_appearanceUi->downloadColorSchemeButton->setConfigFile(QStringLiteral("konsole.knsrc")); |
|
|
|
// setup color list |
|
// select the colorScheme used in the current profile |
|
updateColorSchemeList(currentColorSchemeName()); |
|
|
|
_appearanceUi->colorSchemeList->setMouseTracking(true); |
|
_appearanceUi->colorSchemeList->installEventFilter(this); |
|
_appearanceUi->colorSchemeList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); |
|
|
|
connect(_appearanceUi->colorSchemeList->selectionModel(), |
|
&QItemSelectionModel::selectionChanged, this, |
|
&Konsole::EditProfileDialog::colorSchemeSelected); |
|
connect(_appearanceUi->colorSchemeList, &QListView::entered, 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->downloadColorSchemeButton, &KNS3::Button::dialogFinished, this, |
|
&Konsole::EditProfileDialog::gotNewColorSchemes); |
|
|
|
connect(_appearanceUi->resetColorSchemeButton, &QPushButton::clicked, this, |
|
&Konsole::EditProfileDialog::resetColorScheme); |
|
|
|
connect(_appearanceUi->chooseFontButton, &QAbstractButton::clicked, this, |
|
&EditProfileDialog::showFontDialog); |
|
|
|
// 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())); |
|
|
|
// 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); |
|
|
|
_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); |
|
|
|
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, |
|
QOverload<int>::of(&QSpinBox::valueChanged), this, |
|
&Konsole::EditProfileDialog::terminalMarginChanged); |
|
|
|
_appearanceUi->lineSpacingSpinner->setValue(profile->lineSpacing()); |
|
connect(_appearanceUi->lineSpacingSpinner, |
|
QOverload<int>::of(&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->displayVerticalLine->setChecked(profile->verticalLine()); |
|
connect(_appearanceUi->displayVerticalLine, &QCheckBox::toggled, |
|
this, &EditProfileDialog::setVerticalLine); |
|
|
|
_appearanceUi->displayVerticalLineAtColumn->setValue(profile->verticalLineAtChar()); |
|
connect(_appearanceUi->displayVerticalLineAtColumn, QOverload<int>::of(&QSpinBox::valueChanged), |
|
this, &EditProfileDialog::setVerticalLineColumn); |
|
} |
|
|
|
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) |
|
{ |
|
preview(Profile::UseFontLineCharacters, enable); |
|
updateTempProfileProperty(Profile::UseFontLineCharacters, enable); |
|
} |
|
|
|
void EditProfileDialog::toggleBlinkingCursor(bool enable) |
|
{ |
|
preview(Profile::BlinkingCursorEnabled, enable); |
|
updateTempProfileProperty(Profile::BlinkingCursorEnabled, 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::updateColorSchemeList(const QString &selectedColorSchemeName) |
|
{ |
|
if (_appearanceUi->colorSchemeList->model() == nullptr) { |
|
_appearanceUi->colorSchemeList->setModel(new QStandardItemModel(this)); |
|
} |
|
|
|
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<const ColorScheme *> schemeList = ColorSchemeManager::instance()->allColorSchemes(); |
|
|
|
for (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); |
|
} |
|
} |
|
|
|
bool EditProfileDialog::eventFilter(QObject *watched, QEvent *event) |
|
{ |
|
if (watched == _appearanceUi->colorSchemeList && event->type() == QEvent::Leave) { |
|
if (_tempProfile->isPropertySet(Profile::ColorScheme)) { |
|
preview(Profile::ColorScheme, _tempProfile->colorScheme()); |
|
} else { |
|
unpreview(Profile::ColorScheme); |
|
} |
|
} |
|
|
|
return QDialog::eventFilter(watched, event); |
|
} |
|
|
|
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() |
|
{ |
|
_delayedPreviewTimer->stop(); |
|
_delayedPreviewProperties.clear(); |
|
|
|
QHash<Profile::Property, QVariant> map; |
|
QHashIterator<int, QVariant> iter(_previewedProperties); |
|
while (iter.hasNext()) { |
|
iter.next(); |
|
map.insert(static_cast<Profile::Property>(iter.key()), iter.value()); |
|
} |
|
|
|
// undo any preview changes |
|
if (!map.isEmpty()) { |
|
ProfileManager::instance()->changeProfile(_profile, map, false); |
|
} |
|
} |
|
|
|
void EditProfileDialog::unpreview(int property) |
|
{ |
|
_delayedPreviewProperties.remove(property); |
|
|
|
if (!_previewedProperties.contains(property)) { |
|
return; |
|
} |
|
|
|
QHash<Profile::Property, QVariant> map; |
|
map.insert(static_cast<Profile::Property>(property), _previewedProperties[property]); |
|
ProfileManager::instance()->changeProfile(_profile, map, false); |
|
|
|
_previewedProperties.remove(property); |
|
} |
|
|
|
void EditProfileDialog::delayedPreview(int property, const QVariant &value) |
|
{ |
|
_delayedPreviewProperties.insert(property, value); |
|
|
|
_delayedPreviewTimer->stop(); |
|
_delayedPreviewTimer->start(300); |
|
} |
|
|
|
void EditProfileDialog::delayedPreviewActivate() |
|
{ |
|
Q_ASSERT(qobject_cast<QTimer *>(sender())); |
|
|
|
QMutableHashIterator<int, QVariant> iter(_delayedPreviewProperties); |
|
if (iter.hasNext()) { |
|
iter.next(); |
|
preview(iter.key(), iter.value()); |
|
} |
|
} |
|
|
|
void EditProfileDialog::preview(int property, const QVariant &value) |
|
{ |
|
QHash<Profile::Property, QVariant> map; |
|
map.insert(static_cast<Profile::Property>(property), value); |
|
|
|
_delayedPreviewProperties.remove(property); |
|
|
|
const Profile::Ptr original = lookupProfile(); |
|
|
|
// 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>(static_cast<Profile::Property>(property)).isNull()) { |
|
return; |
|
} |
|
|
|
if (!_previewedProperties.contains(property)) { |
|
_previewedProperties.insert(property, |
|
original->property<QVariant>(static_cast<Profile::Property>(property))); |
|
} |
|
|
|
// temporary change to color scheme |
|
ProfileManager::instance()->changeProfile(_profile, map, false); |
|
} |
|
|
|
void EditProfileDialog::previewColorScheme(const QModelIndex &index) |
|
{ |
|
const QString &name = index.data(Qt::UserRole + 1).value<const ColorScheme *>()->name(); |
|
delayedPreview(Profile::ColorScheme, name); |
|
} |
|
|
|
void EditProfileDialog::showFontDialog() |
|
{ |
|
if (_fontDialog == nullptr) { |
|
_fontDialog = new FontDialog(this); |
|
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); |
|
const QFont font = _profile->font(); |
|
updateFontPreview(font); |
|
}); |
|
} |
|
const QFont font = _profile->font(); |
|
_fontDialog->setFont(font); |
|
_fontDialog->exec(); |
|
} |
|
|
|
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::removeColorScheme() |
|
{ |
|
QModelIndexList selected = _appearanceUi->colorSchemeList->selectionModel()->selectedIndexes(); |
|
if (selected.isEmpty()) { |
|
return; |
|
} |
|
|
|
// The actual delete runs async because we need to on-demand query |
|
// files managed by KNS. Deleting files managed by KNS screws up the |
|
// KNS states (entry gets shown as installed when in fact we deleted it). |
|
auto *manager = new KNSCore::DownloadManager(QStringLiteral("konsole.knsrc"), this); |
|
connect(manager, &KNSCore::DownloadManager::searchResult, |
|
this, [=](const KNSCore::EntryInternal::List &entries) { |
|
const QString &name = selected.first().data(Qt::UserRole + 1).value<const ColorScheme *>()->name(); |
|
Q_ASSERT(!name.isEmpty()); |
|
bool uninstalled = false; |
|
// Check if the theme was installed by KNS, if so uninstall it through |
|
// there and unload it. |
|
for (auto &entry : entries) { |
|
for (const auto &file : entry.installedFiles()) { |
|
if (ColorSchemeManager::colorSchemeNameFromPath(file) != name) { |
|
continue; |
|
} |
|
// Make sure the manager can unload it before uninstalling it. |
|
if (ColorSchemeManager::instance()->unloadColorScheme(file)) { |
|
manager->uninstallEntry(entry); |
|
uninstalled = true; |
|
} |
|
} |
|
if (uninstalled) { |
|
break; |
|
} |
|
} |
|
|
|
// If KNS wasn't able to remove it is a custom theme and we'll drop |
|
// it manually. |
|
if (!uninstalled) { |
|
uninstalled = ColorSchemeManager::instance()->deleteColorScheme(name); |
|
} |
|
|
|
if (uninstalled) { |
|
_appearanceUi->colorSchemeList->model()->removeRow(selected.first().row()); |
|
} |
|
|
|
manager->deleteLater(); |
|
}); |
|
manager->checkForInstalled(); |
|
} |
|
|
|
void EditProfileDialog::gotNewColorSchemes(const KNS3::Entry::List &changedEntries) |
|
{ |
|
int failures = 0; |
|
for (auto &entry : qAsConst(changedEntries)) { |
|
switch (entry.status()) { |
|
case KNS3::Entry::Installed: |
|
for (const auto &file : entry.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 KNS3::Entry::Deleted: |
|
for (const auto &file : entry.uninstalledFiles()) { |
|
if (ColorSchemeManager::instance()->unloadColorScheme(file)) { |
|
continue; |
|
} |
|
qWarning() << "Failed to unload file" << file; |
|
// If unloading fails we do not care. Iff the scheme failed here |
|
// it either wasn't loaded or was invalid to begin with. |
|
} |
|
break; |
|
case KNS3::Entry::Invalid: |
|
case KNS3::Entry::Installing: |
|
case KNS3::Entry::Downloadable: |
|
case KNS3::Entry::Updateable: |
|
case KNS3::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<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(); |
|
const ColorScheme *colors = nullptr; |
|
if (!selected.isEmpty()) { |
|
colors = model->data(selected.first(), Qt::UserRole + 1).value<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(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) |
|
{ |
|
auto newScheme = new 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(); |
|
const auto *colors = model->data(selected.first(), Qt::UserRole + 1).value<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<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<const ColorScheme *>()->opacity() < 1.0; |
|
|
|
if (!needTransparency) { |
|
_appearanceUi->transparencyWarningWidget->setHidden(true); |
|
} else if (!KWindowSystem::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::createTempProfile() |
|
{ |
|
_tempProfile = Profile::Ptr(new Profile); |
|
_tempProfile->setHidden(true); |
|
} |
|
|
|
void EditProfileDialog::updateTempProfileProperty(Profile::Property property, const QVariant &value) |
|
{ |
|
_tempProfile->setProperty(property, value); |
|
updateButtonApply(); |
|
} |
|
|
|
void EditProfileDialog::updateButtonApply() |
|
{ |
|
bool userModified = false; |
|
|
|
QHashIterator<Profile::Property, QVariant> iter(_tempProfile->setProperties()); |
|
while (iter.hasNext()) { |
|
iter.next(); |
|
|
|
Profile::Property property = iter.key(); |
|
QVariant value = iter.value(); |
|
|
|
// for previewed property |
|
if (_previewedProperties.contains(static_cast<int>(property))) { |
|
if (value != _previewedProperties.value(static_cast<int>(property))) { |
|
userModified = true; |
|
break; |
|
} |
|
// 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))) { |
|
userModified = true; |
|
break; |
|
} |
|
} |
|
|
|
_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(userModified); |
|
} |
|
|
|
void EditProfileDialog::setupKeyboardPage(const Profile::Ptr &/* profile */) |
|
{ |
|
// setup translator list |
|
updateKeyBindingsList(lookupProfile()->keyBindings()); |
|
|
|
connect(_keyboardUi->keyBindingList->selectionModel(), |
|
&QItemSelectionModel::selectionChanged, this, |
|
&Konsole::EditProfileDialog::keyBindingSelected); |
|
connect(_keyboardUi->newKeyBindingsButton, &QPushButton::clicked, this, |
|
&Konsole::EditProfileDialog::newKeyBinding); |
|
|
|
_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(); |
|
} |
|
|
|
Q_ASSERT(translator); |
|
|
|
auto editor = new KeyBindingEditor(this); |
|
|
|
if (translator != nullptr) { |
|
editor->setup(translator, lookupProfile()->keyBindings(), isNewTranslator); |
|
} |
|
|
|
connect(editor, &Konsole::KeyBindingEditor::updateKeyBindingsListRequest, |
|
this, &Konsole::EditProfileDialog::updateKeyBindingsList); |
|
|
|
connect(editor, &Konsole::KeyBindingEditor::updateTempProfileKeyBindingsRequest, |
|
this, &Konsole::EditProfileDialog::updateTempProfileProperty); |
|
|
|
editor->exec(); |
|
} |
|
|
|
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); |
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) |
|
connect(options.group, QOverload<int>::of(&QButtonGroup::idClicked), |
|
#else |
|
connect(options.group, QOverload<int>::of(&QButtonGroup::buttonClicked), |
|
#endif |
|
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::scrollFullPage); |
|
|
|
_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); |
|
|
|
// 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::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); |
|
|
|
// 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); |
|
|
|
// 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); |
|
} |
|
|
|
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->enableBidiRenderingButton->setChecked(profile->property<bool>(Profile::BidiRenderingEnabled)); |
|
connect(_advancedUi->enableBidiRenderingButton, &QPushButton::toggled, this, &EditProfileDialog::togglebidiRendering); |
|
_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); |
|
} |
|
|
|
// encoding options |
|
auto codecAction = new KCodecAction(this); |
|
codecAction->setCurrentCodec(profile->defaultEncoding()); |
|
_advancedUi->selectEncodingButton->setMenu(codecAction->menu()); |
|
connect(codecAction, |
|
QOverload<QTextCodec *>::of(&KCodecAction::triggered), this, |
|
&Konsole::EditProfileDialog::setDefaultCodec); |
|
|
|
_advancedUi->selectEncodingButton->setText(profile->defaultEncoding()); |
|
} |
|
|
|
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) |
|
.expandedTo(QApplication::globalStrut()); |
|
|
|
return spinBoxSize.width(); |
|
} |
|
|
|
void EditProfileDialog::setDefaultCodec(QTextCodec *codec) |
|
{ |
|
QString name = QString::fromLocal8Bit(codec->name()); |
|
|
|
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); |
|
} |
|
|
|
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::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); |
|
}
|
|
|