File indexing completed on 2024-05-12 09:57:13
0001 /* 0002 SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com> 0003 SPDX-FileCopyrightText: 2018 Harald Sitter <sitter@kde.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 // Own 0009 #include "EditProfileDialog.h" 0010 0011 // Qt 0012 #include <QDialog> 0013 #include <QDialogButtonBox> 0014 #include <QFileDialog> 0015 #include <QIcon> 0016 #include <QInputDialog> 0017 #include <QMenu> 0018 #include <QPushButton> 0019 #include <QRegularExpressionValidator> 0020 #include <QStandardItem> 0021 #include <QStandardPaths> 0022 #include <QStringListModel> 0023 #include <QTextCodec> 0024 #include <QTimer> 0025 #include <QUrl> 0026 0027 // KDE 0028 #include <KCodecAction> 0029 #include <KIconDialog> 0030 #include <KLocalizedString> 0031 #include <KWindowSystem> 0032 #include <kconfigwidgets_version.h> 0033 0034 #include <KNSWidgets/Button> 0035 0036 // Konsole 0037 #include "ui_EditProfileAdvancedPage.h" 0038 #include "ui_EditProfileAppearancePage.h" 0039 #include "ui_EditProfileGeneralPage.h" 0040 #include "ui_EditProfileKeyboardPage.h" 0041 #include "ui_EditProfileMousePage.h" 0042 #include "ui_EditProfileScrollingPage.h" 0043 #include "ui_EditProfileTabsPage.h" 0044 0045 #include "colorscheme/ColorSchemeManager.h" 0046 0047 #include "keyboardtranslator/KeyboardTranslator.h" 0048 0049 #include "KeyBindingEditor.h" 0050 #include "ShellCommand.h" 0051 #include "WindowSystemInfo.h" 0052 #include "profile/ProfileManager.h" 0053 0054 // Others 0055 #if defined(Q_OS_LINUX) | defined(Q_OS_FREEBSD) | defined(Q_OS_OPENBSD) | defined(Q_OS_SOLARIS) | defined(Q_OS_MACOS) 0056 #include <unistd.h> 0057 #endif 0058 0059 using namespace Konsole; 0060 0061 EditProfileDialog::EditProfileDialog(QWidget *parent) 0062 : KPageDialog(parent) 0063 , _tempProfile(new Profile{}) 0064 { 0065 _tempProfile->setHidden(true); 0066 0067 setWindowTitle(i18n("Edit Profile")); 0068 setFaceType(KPageDialog::List); 0069 0070 _buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply); 0071 setButtonBox(_buttonBox); 0072 0073 _buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); 0074 0075 auto *applyButton = _buttonBox->button(QDialogButtonBox::Apply); 0076 // Disable it, since no modifications have been made yet 0077 applyButton->setEnabled(false); 0078 connect(applyButton, &QPushButton::clicked, this, [this]() { 0079 if (isProfileNameValid()) { 0080 save(); 0081 } 0082 }); 0083 0084 // Set a fallback icon for non-plasma desktops as this dialog looks 0085 // terrible without all the icons on the left sidebar. On GTK related 0086 // desktops, this dialog look good enough without installing 0087 // oxygen-icon-theme, qt5ct and setting export QT_QPA_PLATFORMTHEME=qt5ct 0088 // Plain Xorg desktops still look terrible as there are no icons 0089 // visible. 0090 const auto defaultIcon = QIcon::fromTheme(QStringLiteral("utilities-terminal")); 0091 0092 // General page 0093 0094 const QString generalPageName = i18nc("@title:tab Generic, common options", "General"); 0095 auto *generalPageWidget = new QWidget(this); 0096 _generalUi = new Ui::EditProfileGeneralPage(); 0097 _generalUi->setupUi(generalPageWidget); 0098 _generalPageItem = addPage(generalPageWidget, generalPageName); 0099 _generalPageItem->setHeader(generalPageName); 0100 _generalPageItem->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal"))); 0101 _pages[_generalPageItem] = Page(&EditProfileDialog::setupGeneralPage); 0102 0103 // Tabs page 0104 0105 const QString tabsPageName = i18n("Tabs"); 0106 auto *tabsPageWidget = new QWidget(this); 0107 _tabsUi = new Ui::EditProfileTabsPage(); 0108 _tabsUi->setupUi(tabsPageWidget); 0109 auto *tabsPageItem = addPage(tabsPageWidget, tabsPageName); 0110 tabsPageItem->setHeader(tabsPageName); 0111 tabsPageItem->setIcon(QIcon::fromTheme(QStringLiteral("preferences-tabs"), defaultIcon)); 0112 _pages[tabsPageItem] = Page(&EditProfileDialog::setupTabsPage); 0113 0114 LabelsAligner tabsAligner(tabsPageWidget); 0115 tabsAligner.addLayout(dynamic_cast<QGridLayout *>(_tabsUi->tabMonitoringGroup->layout())); 0116 tabsAligner.addLayout(dynamic_cast<QGridLayout *>(_tabsUi->renameTabWidget->layout())); 0117 tabsAligner.updateLayouts(); 0118 tabsAligner.align(); 0119 0120 // Appearance page 0121 0122 const QString appearancePageName = i18n("Appearance"); 0123 auto *appearancePageWidget = new QWidget(this); 0124 _appearanceUi = new Ui::EditProfileAppearancePage(); 0125 _appearanceUi->setupUi(appearancePageWidget); 0126 auto *appearancePageItem = addPage(appearancePageWidget, appearancePageName); 0127 appearancePageItem->setHeader(appearancePageName); 0128 appearancePageItem->setIcon(QIcon::fromTheme(QStringLiteral("kcolorchooser"), defaultIcon)); 0129 _pages[appearancePageItem] = Page(&EditProfileDialog::setupAppearancePage); 0130 0131 LabelsAligner appearanceAligner(appearancePageWidget); 0132 appearanceAligner.addLayout(dynamic_cast<QGridLayout *>(_appearanceUi->contentsGroup->layout())); 0133 appearanceAligner.updateLayouts(); 0134 appearanceAligner.align(); 0135 0136 // Scrolling page 0137 0138 const QString scrollingPageName = i18n("Scrolling"); 0139 auto *scrollingPageWidget = new QWidget(this); 0140 _scrollingUi = new Ui::EditProfileScrollingPage(); 0141 _scrollingUi->setupUi(scrollingPageWidget); 0142 auto *scrollingPageItem = addPage(scrollingPageWidget, scrollingPageName); 0143 scrollingPageItem->setHeader(scrollingPageName); 0144 scrollingPageItem->setIcon(QIcon::fromTheme(QStringLiteral("preferences-scroll"), defaultIcon)); 0145 _pages[scrollingPageItem] = Page(&EditProfileDialog::setupScrollingPage); 0146 0147 // adjust "history size" label height to match history size widget's first radio button 0148 _scrollingUi->historySizeLabel->setFixedHeight(_scrollingUi->historySizeWidget->preferredLabelHeight()); 0149 0150 // Keyboard page 0151 0152 const QString keyboardPageName = i18n("Keyboard"); 0153 const QString keyboardPageTitle = i18n("Key bindings"); 0154 auto *keyboardPageWidget = new QWidget(this); 0155 _keyboardUi = new Ui::EditProfileKeyboardPage(); 0156 _keyboardUi->setupUi(keyboardPageWidget); 0157 auto *keyboardPageItem = addPage(keyboardPageWidget, keyboardPageName); 0158 keyboardPageItem->setHeader(keyboardPageTitle); 0159 keyboardPageItem->setIcon(QIcon::fromTheme(QStringLiteral("input-keyboard"), defaultIcon)); 0160 _pages[keyboardPageItem] = Page(&EditProfileDialog::setupKeyboardPage); 0161 0162 // Mouse page 0163 0164 const QString mousePageName = i18n("Mouse"); 0165 auto *mousePageWidget = new QWidget(this); 0166 _mouseUi = new Ui::EditProfileMousePage(); 0167 _mouseUi->setupUi(mousePageWidget); 0168 0169 const auto regExp = QRegularExpression(QStringLiteral(R"(([a-z]*:\/\/;)*([A-Za-z*]:\/\/))")); 0170 auto validator = new QRegularExpressionValidator(regExp, this); 0171 _mouseUi->linkEscapeSequenceTexts->setValidator(validator); 0172 0173 auto *mousePageItem = addPage(mousePageWidget, mousePageName); 0174 mousePageItem->setHeader(mousePageName); 0175 mousePageItem->setIcon(QIcon::fromTheme(QStringLiteral("input-mouse"), defaultIcon)); 0176 _pages[mousePageItem] = Page(&EditProfileDialog::setupMousePage); 0177 0178 // Advanced page 0179 0180 const QString advancedPageName = i18nc("@title:tab Complex options", "Advanced"); 0181 auto *advancedPageWidget = new QWidget(this); 0182 _advancedUi = new Ui::EditProfileAdvancedPage(); 0183 _advancedUi->setupUi(advancedPageWidget); 0184 auto *advancedPageItem = addPage(advancedPageWidget, advancedPageName); 0185 advancedPageItem->setHeader(advancedPageName); 0186 advancedPageItem->setIcon(QIcon::fromTheme(QStringLiteral("preferences-other"), defaultIcon)); 0187 _pages[advancedPageItem] = Page(&EditProfileDialog::setupAdvancedPage); 0188 0189 // there are various setupXYZPage() methods to load the items 0190 // for each page and update their states to match the profile 0191 // being edited. 0192 // 0193 // these are only called when needed ( ie. when the user clicks 0194 // the tab to move to that page ). 0195 // 0196 // the _pageNeedsUpdate vector keeps track of the pages that have 0197 // not been updated since the last profile change and will need 0198 // to be refreshed when the user switches to them 0199 connect(this, &KPageDialog::currentPageChanged, this, &Konsole::EditProfileDialog::preparePage); 0200 } 0201 0202 EditProfileDialog::~EditProfileDialog() 0203 { 0204 delete _generalUi; 0205 delete _tabsUi; 0206 delete _appearanceUi; 0207 delete _scrollingUi; 0208 delete _keyboardUi; 0209 delete _mouseUi; 0210 delete _advancedUi; 0211 } 0212 0213 void EditProfileDialog::save() 0214 { 0215 const bool isNewProfile = _profileState == EditProfileDialog::NewProfile; 0216 0217 if (isNewProfile) { 0218 ProfileManager::instance()->addProfile(_profile); 0219 // The profile exists now, no longer treat it as initial creation 0220 _profileState = EditProfileDialog::ExistingProfile; 0221 } 0222 0223 bool defaultChanged = _isDefault != _generalUi->setAsDefaultButton->isChecked(); 0224 0225 if (_tempProfile->isEmpty() && !defaultChanged) { 0226 if (isNewProfile) { 0227 // New profile, we need to save it to disk, even if no settings 0228 // were changed and _tempProfile is empty 0229 ProfileManager::instance()->changeProfile(_profile, _profile->properties()); 0230 } 0231 // no changes since last save 0232 return; 0233 } 0234 0235 ProfileManager::instance()->changeProfile(_profile, _tempProfile->properties()); 0236 0237 // ensure that these settings are not undone by a call 0238 // to unpreview() 0239 _previewedProperties.clear(); 0240 0241 // Update the default profile if needed 0242 if (defaultChanged) { 0243 Q_ASSERT(_profile != ProfileManager::instance()->builtinProfile()); 0244 0245 bool defaultChecked = _generalUi->setAsDefaultButton->isChecked(); 0246 Profile::Ptr newDefault = defaultChecked ? _profile : ProfileManager::instance()->builtinProfile(); 0247 ProfileManager::instance()->setDefaultProfile(newDefault); 0248 _isDefault = defaultChecked; 0249 } 0250 0251 resetTempProfile(); 0252 0253 _buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); 0254 } 0255 0256 void EditProfileDialog::reject() 0257 { 0258 unpreviewAll(); 0259 QDialog::reject(); 0260 } 0261 0262 void EditProfileDialog::accept() 0263 { 0264 if (isProfileNameValid()) { 0265 save(); 0266 unpreviewAll(); 0267 QDialog::accept(); 0268 } 0269 } 0270 0271 void EditProfileDialog::setMessageGeneralPage(const QString &msg) 0272 { 0273 _generalUi->generalPageMessageWidget->setText(msg); 0274 _generalUi->generalPageMessageWidget->setMessageType(KMessageWidget::Error); 0275 setCurrentPage(_generalPageItem); 0276 _generalUi->generalPageMessageWidget->animatedShow(); 0277 } 0278 0279 bool EditProfileDialog::isProfileNameValid() 0280 { 0281 Q_ASSERT(_profile); 0282 Q_ASSERT(_tempProfile); 0283 0284 // check whether the user has enough permissions to save the profile 0285 QFileInfo fileInfo(_profile->path()); 0286 if (fileInfo.exists() && !fileInfo.isWritable() && (!_tempProfile->isPropertySet(Profile::Name) || _tempProfile->name() == _profile->name())) { 0287 setMessageGeneralPage(xi18nc("@info", 0288 "Insufficient permissions to save settings to: <filename>%1</filename>.<nl/>" 0289 "Either change the permissions of that file or set a different name to save " 0290 "the settings to a new profile.", 0291 _profile->path())); 0292 return false; 0293 } 0294 0295 const QList<Profile::Ptr> existingProfiles = ProfileManager::instance()->allProfiles(); 0296 QStringList otherExistingProfileNames; 0297 0298 for (const Profile::Ptr &existingProfile : existingProfiles) { 0299 if (existingProfile->name() != _profile->name()) { 0300 otherExistingProfileNames.append(existingProfile->name()); 0301 } 0302 } 0303 0304 if ((_tempProfile->isPropertySet(Profile::Name) && _tempProfile->name().isEmpty()) || (_profile->name().isEmpty() && _tempProfile->name().isEmpty())) { 0305 setMessageGeneralPage(i18nc("@info", "Profile Name was empty; please set a name to be able to save settings.")); 0306 // Revert the name in the dialog 0307 _generalUi->profileNameEdit->setText(_profile->name()); 0308 selectProfileName(); 0309 return false; 0310 } 0311 0312 if (!_tempProfile->name().isEmpty() && otherExistingProfileNames.contains(_tempProfile->name())) { 0313 setMessageGeneralPage(i18nc("@info", "A profile with the name \"%1\" already exists.", _generalUi->profileNameEdit->text())); 0314 // Revert the name in the dialog 0315 _generalUi->profileNameEdit->setText(_profile->name()); 0316 selectProfileName(); 0317 return false; 0318 } 0319 0320 const QString profileStoragePath(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + QStringLiteral("konsole")); 0321 int nameLenMax = 256; 0322 0323 #if defined(Q_OS_LINUX) | defined(Q_OS_OPENBSD) | defined(Q_OS_FREEBSD) | defined(Q_OS_MACOS) | defined(Q_OS_SOLARIS) 0324 const int maxPath = pathconf(profileStoragePath.toLocal8Bit().constData(), _PC_PATH_MAX); 0325 const int nameLenMaxPath = maxPath - _profile->path().lastIndexOf(QLatin1Char('/')) + 1; 0326 const int nameLenMaxName = pathconf(profileStoragePath.toLocal8Bit().constData(), _PC_NAME_MAX); 0327 nameLenMax = qMin(nameLenMaxPath, nameLenMaxName); 0328 #elif defined(Q_OS_WIN) 0329 // If MAX_PATH not found, use default (260 - 1) 0330 const int maxPath = qMax(259, qEnvironmentVariable("MAX_PATH").toInt()); 0331 nameLenMax = maxPath - _profile->path().lastIndexOf(QLatin1Char('/')) + 1; 0332 #endif 0333 0334 nameLenMax -= QStringLiteral(".profile").size(); 0335 0336 if (_tempProfile->name().size() > nameLenMax) { 0337 setMessageGeneralPage(i18nc("@info", "Profile name exceeded maximum allowed length (%1).", nameLenMax)); 0338 // Revert the name in the dialog 0339 _generalUi->profileNameEdit->setText(_profile->name()); 0340 selectProfileName(); 0341 return false; 0342 } 0343 0344 if (!_tempProfile->name().isEmpty()) { 0345 static const QString illegalCharacters(QStringLiteral("#%&{}/\\\"<>*?$!':@`+=|")); 0346 static const QString colorTagOpen(QStringLiteral("<font color=\"red\">")); 0347 static const QString colorTagClose(QStringLiteral("</font>")); 0348 const QString name(_tempProfile->name()); 0349 bool hasIllegal = false; 0350 bool unclosedTag = false; 0351 QString highlightedName; 0352 0353 for (int i = 0; i < name.size(); ++i) { 0354 const auto currChar = name.at(i); 0355 0356 if (illegalCharacters.contains(currChar)) { 0357 if (!unclosedTag) { 0358 highlightedName += colorTagOpen; 0359 } 0360 hasIllegal = true; 0361 unclosedTag = true; 0362 } else if (unclosedTag) { 0363 unclosedTag = false; 0364 highlightedName += colorTagClose; 0365 } 0366 0367 highlightedName += currChar; 0368 0369 if (i + 1 == name.size() && unclosedTag) { 0370 highlightedName += colorTagClose; 0371 } 0372 } 0373 0374 if (hasIllegal) { 0375 setMessageGeneralPage(i18nc("@info", "The highlighted characters are invalid in profile names: %1.", highlightedName)); 0376 // Revert the name in the dialog 0377 _generalUi->profileNameEdit->setText(_profile->name()); 0378 selectProfileName(); 0379 return false; 0380 } 0381 } 0382 0383 // Valid name 0384 return true; 0385 } 0386 0387 QString EditProfileDialog::groupProfileNames(const ProfileGroup::Ptr &group, int maxLength) 0388 { 0389 QString caption; 0390 int count = group->profiles().count(); 0391 for (int i = 0; i < count; i++) { 0392 caption += group->profiles()[i]->name(); 0393 if (i < (count - 1)) { 0394 caption += QLatin1Char(','); 0395 // limit caption length to prevent very long window titles 0396 if (maxLength > 0 && caption.length() > maxLength) { 0397 caption += QLatin1String("..."); 0398 break; 0399 } 0400 } 0401 } 0402 return caption; 0403 } 0404 0405 void EditProfileDialog::updateCaption(const Profile::Ptr &profile) 0406 { 0407 const int MAX_GROUP_CAPTION_LENGTH = 25; 0408 ProfileGroup::Ptr group = profile->asGroup(); 0409 if (group && group->profiles().count() > 1) { 0410 QString caption = groupProfileNames(group, MAX_GROUP_CAPTION_LENGTH); 0411 setWindowTitle(i18np("Editing profile: %2", "Editing %1 profiles: %2", group->profiles().count(), caption)); 0412 updateButtonApply(); 0413 } else { 0414 if (_profileState == EditProfileDialog::NewProfile) { 0415 setWindowTitle(i18n("Create New Profile")); 0416 _buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); 0417 } else { 0418 setWindowTitle(i18n("Edit Profile \"%1\"", profile->name())); 0419 updateButtonApply(); 0420 } 0421 } 0422 } 0423 0424 void EditProfileDialog::setProfile(const Konsole::Profile::Ptr &profile, EditProfileDialog::InitialProfileState state) 0425 { 0426 Q_ASSERT(profile); 0427 0428 _profile = profile; 0429 0430 _profileState = state; 0431 0432 // update caption 0433 updateCaption(profile); 0434 0435 // mark each page of the dialog as out of date 0436 // and force an update of the currently visible page 0437 // 0438 // the other pages will be updated as necessary 0439 for (Page &page : _pages) { 0440 page.needsUpdate = true; 0441 } 0442 preparePage(currentPage()); 0443 0444 resetTempProfile(); 0445 } 0446 0447 const QString EditProfileDialog::currentColorSchemeName() const 0448 { 0449 const QString ¤tColorSchemeName = _profile->colorScheme(); 0450 return currentColorSchemeName; 0451 } 0452 0453 void EditProfileDialog::preparePage(KPageWidgetItem *current, KPageWidgetItem *before) 0454 { 0455 Q_UNUSED(before) 0456 Q_ASSERT(current); 0457 Q_ASSERT(_pages.contains(current)); 0458 0459 const Profile::Ptr profile = _profile; 0460 auto setupPage = _pages[current].setupPage; 0461 Q_ASSERT(profile); 0462 Q_ASSERT(setupPage); 0463 0464 if (_pages[current].needsUpdate) { 0465 (*this.*setupPage)(profile); 0466 _pages[current].needsUpdate = false; 0467 } 0468 } 0469 0470 void Konsole::EditProfileDialog::selectProfileName() 0471 { 0472 _generalUi->profileNameEdit->setFocus(); 0473 _generalUi->profileNameEdit->selectAll(); 0474 } 0475 0476 void EditProfileDialog::setupGeneralPage(const Profile::Ptr &profile) 0477 { 0478 _generalUi->generalPageMessageWidget->setVisible(false); 0479 _generalUi->generalPageMessageWidget->setWordWrap(true); 0480 _generalUi->generalPageMessageWidget->setCloseButtonVisible(true); 0481 0482 // basic profile options 0483 { 0484 ProfileGroup::Ptr group = profile->asGroup(); 0485 if (!group || group->profiles().count() < 2) { 0486 _generalUi->profileNameEdit->setText(profile->name()); 0487 _generalUi->profileNameEdit->setClearButtonEnabled(true); 0488 } else { 0489 _generalUi->profileNameEdit->setText(groupProfileNames(group, -1)); 0490 _generalUi->profileNameEdit->setEnabled(false); 0491 } 0492 } 0493 0494 ShellCommand command(profile->command(), profile->arguments()); 0495 _generalUi->commandEdit->setText(command.fullCommand()); 0496 // If a "completion" is requested, consider changing this to KLineEdit 0497 // and using KCompletion. 0498 _generalUi->initialDirEdit->setText(profile->defaultWorkingDirectory()); 0499 _generalUi->initialDirEdit->setClearButtonEnabled(true); 0500 _generalUi->initialDirEdit->setPlaceholderText(QStandardPaths::standardLocations(QStandardPaths::HomeLocation).value(0)); 0501 0502 _generalUi->dirSelectButton->setIcon(QIcon::fromTheme(QStringLiteral("folder-open"))); 0503 _generalUi->iconSelectButton->setIcon(QIcon::fromTheme(profile->icon())); 0504 _generalUi->environmentEditButton->setIcon(QIcon::fromTheme(QStringLiteral("document-edit"))); 0505 _generalUi->startInSameDirButton->setChecked(profile->startInCurrentSessionDir()); 0506 0507 // initial terminal size 0508 const auto colsSuffix = 0509 ki18ncp("Suffix of the number of columns (N columns). The leading space is needed to separate it from the number value.", " column", " columns"); 0510 const auto rowsSuffix = 0511 ki18ncp("Suffix of the number of rows (N rows). The leading space is needed to separate it from the number value.", " row", " rows"); 0512 _generalUi->terminalColumnsEntry->setValue(profile->terminalColumns()); 0513 _generalUi->terminalRowsEntry->setValue(profile->terminalRows()); 0514 _generalUi->terminalColumnsEntry->setSuffix(colsSuffix); 0515 _generalUi->terminalRowsEntry->setSuffix(rowsSuffix); 0516 // make width of initial terminal size spinboxes equal 0517 const int sizeEntryWidth = qMax(maxSpinBoxWidth(_generalUi->terminalColumnsEntry, colsSuffix), maxSpinBoxWidth(_generalUi->terminalRowsEntry, rowsSuffix)); 0518 _generalUi->terminalColumnsEntry->setFixedWidth(sizeEntryWidth); 0519 _generalUi->terminalRowsEntry->setFixedWidth(sizeEntryWidth); 0520 0521 auto *bellModeModel = new QStringListModel({i18n("System Bell"), i18n("System Notifications"), i18n("Visual Bell"), i18n("Ignore Bell Events")}, this); 0522 _generalUi->terminalBellCombo->setModel(bellModeModel); 0523 _generalUi->terminalBellCombo->setCurrentIndex(profile->property<int>(Profile::BellMode)); 0524 0525 _isDefault = profile == ProfileManager::instance()->defaultProfile(); 0526 _generalUi->setAsDefaultButton->setChecked(_isDefault); 0527 QString appName = QCoreApplication::applicationName(); 0528 if (!appName.isEmpty() && appName != QLatin1String("konsole")) { 0529 appName[0] = appName.at(0).toUpper(); 0530 _generalUi->setAsDefaultButton->setText(i18n("Default profile for new terminal sessions in %1", appName)); 0531 } else { 0532 _generalUi->setAsDefaultButton->setText(i18n("Default profile")); 0533 } 0534 _generalUi->semanticUpDown->setChecked(profile->semanticUpDown()); 0535 _generalUi->semanticInputClick->setChecked(profile->semanticInputClick()); 0536 0537 // signals and slots 0538 connect(_generalUi->dirSelectButton, &QToolButton::clicked, this, &Konsole::EditProfileDialog::selectInitialDir); 0539 connect(_generalUi->iconSelectButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::selectIcon); 0540 connect(_generalUi->startInSameDirButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::startInSameDir); 0541 connect(_generalUi->profileNameEdit, &QLineEdit::textChanged, this, &Konsole::EditProfileDialog::profileNameChanged); 0542 connect(_generalUi->initialDirEdit, &QLineEdit::textChanged, this, &Konsole::EditProfileDialog::initialDirChanged); 0543 connect(_generalUi->commandEdit, &QLineEdit::textChanged, this, &Konsole::EditProfileDialog::commandChanged); 0544 connect(_generalUi->environmentEditButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::showEnvironmentEditor); 0545 connect(_generalUi->semanticUpDown, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::semanticUpDown); 0546 connect(_generalUi->semanticInputClick, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::semanticInputClick); 0547 0548 connect(_generalUi->terminalColumnsEntry, &QSpinBox::valueChanged, this, &Konsole::EditProfileDialog::terminalColumnsEntryChanged); 0549 connect(_generalUi->terminalRowsEntry, &QSpinBox::valueChanged, this, &Konsole::EditProfileDialog::terminalRowsEntryChanged); 0550 0551 connect(_generalUi->terminalBellCombo, &QComboBox::currentIndexChanged, this, [this](const int index) { 0552 updateTempProfileProperty(Profile::BellMode, index); 0553 }); 0554 0555 connect(_generalUi->setAsDefaultButton, &QAbstractButton::toggled, this, &Konsole::EditProfileDialog::updateButtonApply); 0556 const ButtonGroupOptions semanticHints = { 0557 _generalUi->semanticHints, // group 0558 Profile::SemanticHints, // profileProperty 0559 false, // preview 0560 { 0561 // buttons 0562 {_generalUi->semanticHintsNever, Enum::HintsNever}, 0563 {_generalUi->semanticHintsURL, Enum::HintsURL}, 0564 {_generalUi->semanticHintsAlways, Enum::HintsAlways}, 0565 }, 0566 }; 0567 setupButtonGroup(semanticHints, profile); 0568 const ButtonGroupOptions errorBars = { 0569 _generalUi->errorBars, // group 0570 Profile::ErrorBars, // profileProperty 0571 false, // preview 0572 { 0573 // buttons 0574 {_generalUi->errorBarsNever, Enum::HintsNever}, 0575 {_generalUi->errorBarsURL, Enum::HintsURL}, 0576 {_generalUi->errorBarsAlways, Enum::HintsAlways}, 0577 }, 0578 }; 0579 setupButtonGroup(errorBars, profile); 0580 const ButtonGroupOptions errorBackground = { 0581 _generalUi->errorBackground, // group 0582 Profile::ErrorBackground, // profileProperty 0583 false, // preview 0584 { 0585 // buttons 0586 {_generalUi->errorBackgroundNever, Enum::HintsNever}, 0587 {_generalUi->errorBackgroundURL, Enum::HintsURL}, 0588 {_generalUi->errorBackgroundAlways, Enum::HintsAlways}, 0589 }, 0590 }; 0591 setupButtonGroup(errorBackground, profile); 0592 const ButtonGroupOptions alternatingBars = { 0593 _generalUi->alternatingBars, // group 0594 Profile::AlternatingBars, // profileProperty 0595 false, // preview 0596 { 0597 // buttons 0598 {_generalUi->alternatingBarsNever, Enum::HintsNever}, 0599 {_generalUi->alternatingBarsURL, Enum::HintsURL}, 0600 {_generalUi->alternatingBarsAlways, Enum::HintsAlways}, 0601 }, 0602 }; 0603 setupButtonGroup(alternatingBars, profile); 0604 const ButtonGroupOptions alternatingBackground = { 0605 _generalUi->alternatingBackground, // group 0606 Profile::AlternatingBackground, // profileProperty 0607 false, // preview 0608 { 0609 // buttons 0610 {_generalUi->alternatingBackgroundNever, Enum::HintsNever}, 0611 {_generalUi->alternatingBackgroundURL, Enum::HintsURL}, 0612 {_generalUi->alternatingBackgroundAlways, Enum::HintsAlways}, 0613 }, 0614 }; 0615 setupButtonGroup(alternatingBackground, profile); 0616 } 0617 0618 void EditProfileDialog::showEnvironmentEditor() 0619 { 0620 bool ok; 0621 const Profile::Ptr profile = _profile; 0622 0623 QStringList currentEnvironment; 0624 0625 // The user could re-open the environment editor before clicking 0626 // OK/Apply in the parent edit profile dialog, so we make sure 0627 // to show the new environment vars 0628 if (_tempProfile->isPropertySet(Profile::Environment)) { 0629 currentEnvironment = _tempProfile->environment(); 0630 } else { 0631 currentEnvironment = profile->environment(); 0632 } 0633 0634 QString text = QInputDialog::getMultiLineText(this, 0635 i18n("Edit Environment"), 0636 i18n("One environment variable per line"), 0637 currentEnvironment.join(QStringLiteral("\n")), 0638 &ok); 0639 0640 QStringList newEnvironment; 0641 0642 if (ok) { 0643 if (!text.isEmpty()) { 0644 newEnvironment = text.split(QLatin1Char('\n')); 0645 updateTempProfileProperty(Profile::Environment, newEnvironment); 0646 } else { 0647 // the user could have removed all entries so we return an empty list 0648 updateTempProfileProperty(Profile::Environment, newEnvironment); 0649 } 0650 } 0651 } 0652 0653 void EditProfileDialog::setupTabsPage(const Profile::Ptr &profile) 0654 { 0655 // tab title format 0656 _tabsUi->renameTabWidget->setTabTitleText(profile->localTabTitleFormat()); 0657 _tabsUi->renameTabWidget->setRemoteTabTitleText(profile->remoteTabTitleFormat()); 0658 _tabsUi->renameTabWidget->setColor(profile->tabColor()); 0659 0660 connect(_tabsUi->renameTabWidget, &Konsole::RenameTabWidget::tabTitleFormatChanged, this, &Konsole::EditProfileDialog::tabTitleFormatChanged); 0661 connect(_tabsUi->renameTabWidget, &Konsole::RenameTabWidget::remoteTabTitleFormatChanged, this, &Konsole::EditProfileDialog::remoteTabTitleFormatChanged); 0662 connect(_tabsUi->renameTabWidget, &Konsole::RenameTabWidget::tabColorChanged, this, &Konsole::EditProfileDialog::tabColorChanged); 0663 0664 // tab monitoring 0665 const int silenceSeconds = profile->silenceSeconds(); 0666 _tabsUi->silenceSecondsSpinner->setValue(silenceSeconds); 0667 auto suffix = ki18ncp("Unit of time", " second", " seconds"); 0668 _tabsUi->silenceSecondsSpinner->setSuffix(suffix); 0669 int silenceCheckBoxWidth = maxSpinBoxWidth(_generalUi->terminalColumnsEntry, suffix); 0670 _tabsUi->silenceSecondsSpinner->setFixedWidth(silenceCheckBoxWidth); 0671 0672 connect(_tabsUi->silenceSecondsSpinner, &QSpinBox::valueChanged, this, &Konsole::EditProfileDialog::silenceSecondsChanged); 0673 } 0674 0675 void EditProfileDialog::terminalColumnsEntryChanged(int value) 0676 { 0677 updateTempProfileProperty(Profile::TerminalColumns, value); 0678 } 0679 0680 void EditProfileDialog::terminalRowsEntryChanged(int value) 0681 { 0682 updateTempProfileProperty(Profile::TerminalRows, value); 0683 } 0684 0685 void EditProfileDialog::showTerminalSizeHint(bool value) 0686 { 0687 updateTempProfileProperty(Profile::ShowTerminalSizeHint, value); 0688 } 0689 0690 void EditProfileDialog::setDimWhenInactive(bool value) 0691 { 0692 updateTempProfileProperty(Profile::DimWhenInactive, value); 0693 } 0694 0695 void EditProfileDialog::setDimValue(int value) 0696 { 0697 updateTempProfileProperty(Profile::DimValue, value); 0698 } 0699 0700 void EditProfileDialog::setBorderWhenActive(bool value) 0701 { 0702 updateTempProfileProperty(Profile::BorderWhenActive, value); 0703 } 0704 0705 void EditProfileDialog::setFocusBorderColor(const QColor &color) 0706 { 0707 updateTempProfileProperty(Profile::FocusBorderColor, color); 0708 } 0709 0710 void EditProfileDialog::tabTitleFormatChanged(const QString &format) 0711 { 0712 updateTempProfileProperty(Profile::LocalTabTitleFormat, format); 0713 } 0714 0715 void EditProfileDialog::remoteTabTitleFormatChanged(const QString &format) 0716 { 0717 updateTempProfileProperty(Profile::RemoteTabTitleFormat, format); 0718 } 0719 0720 void EditProfileDialog::tabColorChanged(const QColor &color) 0721 { 0722 updateTempProfileProperty(Profile::TabColor, color); 0723 } 0724 0725 void EditProfileDialog::focusBorderColorChanged(const QColor &color) 0726 { 0727 updateTempProfileProperty(Profile::FocusBorderColor, color); 0728 } 0729 0730 void EditProfileDialog::silenceSecondsChanged(int seconds) 0731 { 0732 updateTempProfileProperty(Profile::SilenceSeconds, seconds); 0733 } 0734 0735 void EditProfileDialog::selectIcon() 0736 { 0737 const QString &icon = KIconDialog::getIcon(KIconLoader::Desktop, KIconLoader::Application, false, 0, false, this); 0738 if (!icon.isEmpty()) { 0739 _generalUi->iconSelectButton->setIcon(QIcon::fromTheme(icon)); 0740 updateTempProfileProperty(Profile::Icon, icon); 0741 } 0742 } 0743 0744 void EditProfileDialog::profileNameChanged(const QString &name) 0745 { 0746 updateTempProfileProperty(Profile::Name, name); 0747 updateTempProfileProperty(Profile::UntranslatedName, name); 0748 updateCaption(_tempProfile); 0749 } 0750 0751 void EditProfileDialog::startInSameDir(bool sameDir) 0752 { 0753 updateTempProfileProperty(Profile::StartInCurrentSessionDir, sameDir); 0754 } 0755 0756 void EditProfileDialog::semanticUpDown(bool enable) 0757 { 0758 updateTempProfileProperty(Profile::SemanticUpDown, enable); 0759 } 0760 0761 void EditProfileDialog::semanticInputClick(bool enable) 0762 { 0763 updateTempProfileProperty(Profile::SemanticInputClick, enable); 0764 } 0765 0766 void EditProfileDialog::initialDirChanged(const QString &dir) 0767 { 0768 updateTempProfileProperty(Profile::Directory, dir); 0769 } 0770 0771 void EditProfileDialog::commandChanged(const QString &command) 0772 { 0773 ShellCommand shellCommand(command); 0774 0775 updateTempProfileProperty(Profile::Command, shellCommand.command()); 0776 updateTempProfileProperty(Profile::Arguments, shellCommand.arguments()); 0777 } 0778 0779 void EditProfileDialog::selectInitialDir() 0780 { 0781 const QUrl url = QFileDialog::getExistingDirectoryUrl(this, i18n("Select Initial Directory"), QUrl::fromUserInput(_generalUi->initialDirEdit->text())); 0782 0783 if (!url.isEmpty()) { 0784 _generalUi->initialDirEdit->setText(url.path()); 0785 } 0786 } 0787 0788 void EditProfileDialog::setupAppearancePage(const Profile::Ptr &profile) 0789 { 0790 auto compositingActiveHelper = [] { 0791 return WindowSystemInfo::compositingActive(); 0792 }; 0793 auto delegate = new ColorSchemeViewDelegate(compositingActiveHelper, this); 0794 _appearanceUi->colorSchemeList->setItemDelegate(delegate); 0795 0796 _appearanceUi->transparencyWarningWidget->setVisible(false); 0797 _appearanceUi->transparencyWarningWidget->setWordWrap(true); 0798 _appearanceUi->transparencyWarningWidget->setCloseButtonVisible(false); 0799 _appearanceUi->transparencyWarningWidget->setMessageType(KMessageWidget::Warning); 0800 0801 _appearanceUi->colorSchemeMessageWidget->setVisible(false); 0802 _appearanceUi->colorSchemeMessageWidget->setWordWrap(true); 0803 _appearanceUi->colorSchemeMessageWidget->setCloseButtonVisible(false); 0804 _appearanceUi->colorSchemeMessageWidget->setMessageType(KMessageWidget::Warning); 0805 0806 _appearanceUi->editColorSchemeButton->setIcon(QIcon::fromTheme(QStringLiteral("document-edit"))); 0807 _appearanceUi->removeColorSchemeButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); 0808 _appearanceUi->newColorSchemeButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); 0809 _appearanceUi->chooseFontButton->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-font"))); 0810 _appearanceUi->resetColorSchemeButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo"))); 0811 0812 _appearanceUi->editColorSchemeButton->setEnabled(false); 0813 _appearanceUi->removeColorSchemeButton->setEnabled(false); 0814 _appearanceUi->resetColorSchemeButton->setEnabled(false); 0815 0816 // setup color list 0817 // select the colorScheme used in the current profile 0818 updateColorSchemeList(currentColorSchemeName()); 0819 0820 _appearanceUi->colorSchemeList->setMouseTracking(true); 0821 _appearanceUi->colorSchemeList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); 0822 0823 connect(_appearanceUi->colorSchemeList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Konsole::EditProfileDialog::colorSchemeSelected); 0824 connect(_appearanceUi->colorSchemeList, &QListView::activated, this, &Konsole::EditProfileDialog::previewColorScheme); 0825 0826 updateColorSchemeButtons(); 0827 0828 connect(_appearanceUi->editColorSchemeButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::editColorScheme); 0829 connect(_appearanceUi->removeColorSchemeButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::removeColorScheme); 0830 connect(_appearanceUi->newColorSchemeButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::newColorScheme); 0831 0832 connect(_appearanceUi->resetColorSchemeButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::resetColorScheme); 0833 0834 connect(_appearanceUi->chooseFontButton, &QAbstractButton::clicked, this, &EditProfileDialog::showFontDialog); 0835 0836 connect(_appearanceUi->emojiFontPreview, &QAbstractButton::clicked, this, &EditProfileDialog::showEmojiFontDialog); 0837 0838 // setup font preview 0839 const bool antialias = profile->antiAliasFonts(); 0840 0841 QFont profileFont = profile->font(); 0842 profileFont.setStyleStrategy(antialias ? QFont::PreferAntialias : QFont::NoAntialias); 0843 0844 _appearanceUi->fontPreview->setFont(profileFont); 0845 _appearanceUi->fontPreview->setText(QStringLiteral("%1 %2pt").arg(profileFont.family()).arg(profileFont.pointSize())); 0846 0847 QFont emojiFont = profile->emojiFont(); 0848 _appearanceUi->emojiFontPreview->setFont(profileFont); 0849 _appearanceUi->emojiFontPreview->setText(QStringLiteral("%1 %2pt").arg(emojiFont.family()).arg(emojiFont.pointSize())); 0850 0851 // setup font smoothing 0852 _appearanceUi->antialiasTextButton->setChecked(antialias); 0853 connect(_appearanceUi->antialiasTextButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::setAntialiasText); 0854 0855 _appearanceUi->boldIntenseButton->setChecked(profile->boldIntense()); 0856 connect(_appearanceUi->boldIntenseButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::setBoldIntense); 0857 0858 _appearanceUi->useFontLineCharactersButton->setChecked(profile->useFontLineCharacters()); 0859 connect(_appearanceUi->useFontLineCharactersButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::useFontLineCharacters); 0860 0861 _mouseUi->enableMouseWheelZoomButton->setChecked(profile->mouseWheelZoomEnabled()); 0862 connect(_mouseUi->enableMouseWheelZoomButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::toggleMouseWheelZoom); 0863 0864 // cursor options 0865 _appearanceUi->enableBlinkingCursorButton->setChecked(profile->property<bool>(Profile::BlinkingCursorEnabled)); 0866 connect(_appearanceUi->enableBlinkingCursorButton, &QToolButton::toggled, this, &EditProfileDialog::toggleBlinkingCursor); 0867 0868 if (profile->useCustomCursorColor()) { 0869 _appearanceUi->customCursorColorButton->setChecked(true); 0870 } else { 0871 _appearanceUi->autoCursorColorButton->setChecked(true); 0872 } 0873 0874 _appearanceUi->customColorSelectButton->setColor(profile->customCursorColor()); 0875 _appearanceUi->customTextColorSelectButton->setColor(profile->customCursorTextColor()); 0876 0877 connect(_appearanceUi->customCursorColorButton, &QRadioButton::clicked, this, &Konsole::EditProfileDialog::customCursorColor); 0878 connect(_appearanceUi->autoCursorColorButton, &QRadioButton::clicked, this, &Konsole::EditProfileDialog::autoCursorColor); 0879 connect(_appearanceUi->customColorSelectButton, &KColorButton::changed, this, &Konsole::EditProfileDialog::customCursorColorChanged); 0880 connect(_appearanceUi->customTextColorSelectButton, &KColorButton::changed, this, &Konsole::EditProfileDialog::customCursorTextColorChanged); 0881 0882 const ButtonGroupOptions cursorShapeOptions = { 0883 _appearanceUi->cursorShape, // group 0884 Profile::CursorShape, // profileProperty 0885 true, // preview 0886 { 0887 // buttons 0888 {_appearanceUi->cursorShapeBlock, Enum::BlockCursor}, 0889 {_appearanceUi->cursorShapeIBeam, Enum::IBeamCursor}, 0890 {_appearanceUi->cursorShapeUnderline, Enum::UnderlineCursor}, 0891 }, 0892 }; 0893 setupButtonGroup(cursorShapeOptions, profile); 0894 0895 _appearanceUi->marginsSpinner->setValue(profile->terminalMargin()); 0896 connect(_appearanceUi->marginsSpinner, &QSpinBox::valueChanged, this, &Konsole::EditProfileDialog::terminalMarginChanged); 0897 0898 _appearanceUi->lineSpacingSpinner->setValue(profile->lineSpacing()); 0899 connect(_appearanceUi->lineSpacingSpinner, &QSpinBox::valueChanged, this, &Konsole::EditProfileDialog::lineSpacingChanged); 0900 0901 _appearanceUi->alignToCenterButton->setChecked(profile->terminalCenter()); 0902 connect(_appearanceUi->alignToCenterButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::setTerminalCenter); 0903 0904 _appearanceUi->showTerminalSizeHintButton->setChecked(profile->showTerminalSizeHint()); 0905 connect(_appearanceUi->showTerminalSizeHintButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::showTerminalSizeHint); 0906 0907 _appearanceUi->dimWhenInactiveCheckbox->setChecked(profile->dimWhenInactive()); 0908 connect(_appearanceUi->dimWhenInactiveCheckbox, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::setDimWhenInactive); 0909 0910 _appearanceUi->dimValue->setValue(profile->dimValue()); 0911 _appearanceUi->dimValue->setEnabled(profile->dimWhenInactive()); 0912 _appearanceUi->dimLabel->setEnabled(profile->dimWhenInactive()); 0913 connect(_appearanceUi->dimValue, &QSlider::valueChanged, this, &Konsole::EditProfileDialog::setDimValue); 0914 0915 _appearanceUi->borderWhenActiveCheckbox->setChecked(profile->borderWhenActive()); 0916 connect(_appearanceUi->borderWhenActiveCheckbox, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::setBorderWhenActive); 0917 0918 _appearanceUi->focusBorderColor->setColor(profile->focusBorderColor()); 0919 _appearanceUi->focusBorderColor->setEnabled(profile->borderWhenActive()); 0920 _appearanceUi->borderLabel->setEnabled(profile->borderWhenActive()); 0921 connect(_appearanceUi->focusBorderColor, &KColorButton::changed, this, &Konsole::EditProfileDialog::focusBorderColorChanged); 0922 0923 _appearanceUi->invertSelectionColorsCheckbox->setChecked(profile->property<bool>(Profile::InvertSelectionColors)); 0924 connect(_appearanceUi->invertSelectionColorsCheckbox, &QCheckBox::toggled, this, [this](bool checked) { 0925 updateTempProfileProperty(Profile::InvertSelectionColors, checked); 0926 }); 0927 0928 _appearanceUi->displayVerticalLine->setChecked(profile->verticalLine()); 0929 connect(_appearanceUi->displayVerticalLine, &QCheckBox::toggled, this, &EditProfileDialog::setVerticalLine); 0930 0931 _appearanceUi->displayVerticalLineAtColumn->setValue(profile->verticalLineAtChar()); 0932 connect(_appearanceUi->displayVerticalLineAtColumn, &QSpinBox::valueChanged, this, &EditProfileDialog::setVerticalLineColumn); 0933 0934 auto *getNewButton = new KNSWidgets::Button(this); 0935 connect(getNewButton, &KNSWidgets::Button::dialogFinished, this, &Konsole::EditProfileDialog::gotNewColorSchemes); 0936 0937 getNewButton->setText(QStringLiteral("Get New...")); 0938 getNewButton->setConfigFile(QStringLiteral("konsole.knsrc")); 0939 _appearanceUi->colorSchemesBtnLayout->addWidget(getNewButton); 0940 0941 _appearanceUi->enableBidiRenderingButton->setChecked(profile->bidiRenderingEnabled()); 0942 connect(_appearanceUi->enableBidiRenderingButton, &QPushButton::toggled, this, &EditProfileDialog::togglebidiRendering); 0943 0944 _appearanceUi->enableBidiTableDirOverrideButton->setChecked(profile->property<bool>(Profile::BidiTableDirOverride)); 0945 connect(_appearanceUi->enableBidiTableDirOverrideButton, &QPushButton::toggled, this, &EditProfileDialog::togglebidiTableDirOverride); 0946 _appearanceUi->enableBidiTableDirOverrideButton->setEnabled(profile->bidiRenderingEnabled()); 0947 0948 _appearanceUi->bidiLineLTR->setChecked(profile->property<bool>(Profile::BidiLineLTR)); 0949 connect(_appearanceUi->bidiLineLTR, &QPushButton::toggled, this, &EditProfileDialog::togglebidiLineLTR); 0950 _appearanceUi->bidiLineLTR->setEnabled(profile->bidiRenderingEnabled()); 0951 0952 _appearanceUi->wordMode->setChecked(profile->property<bool>(Profile::WordMode)); 0953 connect(_appearanceUi->wordMode, &QPushButton::toggled, this, &EditProfileDialog::toggleWordMode); 0954 _appearanceUi->wordModeAttr->setEnabled(profile->property<bool>(Profile::WordMode)); 0955 _appearanceUi->wordModeAscii->setEnabled(profile->property<bool>(Profile::WordMode)); 0956 _appearanceUi->wordModeBrahmic->setEnabled(profile->property<bool>(Profile::WordMode)); 0957 0958 _appearanceUi->wordModeAttr->setChecked(profile->property<bool>(Profile::WordModeAttr)); 0959 connect(_appearanceUi->wordModeAttr, &QPushButton::toggled, this, &EditProfileDialog::toggleWordModeAttr); 0960 0961 _appearanceUi->wordModeAscii->setChecked(profile->property<bool>(Profile::WordModeAscii)); 0962 connect(_appearanceUi->wordModeAscii, &QPushButton::toggled, this, &EditProfileDialog::toggleWordModeAscii); 0963 0964 _appearanceUi->wordModeBrahmic->setChecked(profile->property<bool>(Profile::WordModeBrahmic)); 0965 connect(_appearanceUi->wordModeBrahmic, &QPushButton::toggled, this, &EditProfileDialog::toggleWordModeBrahmic); 0966 0967 _appearanceUi->ignoreWcWidth->setChecked(profile->property<bool>(Profile::IgnoreWcWidth)); 0968 connect(_appearanceUi->ignoreWcWidth, &QPushButton::toggled, this, &EditProfileDialog::toggleIgnoreWcWidth); 0969 } 0970 0971 void EditProfileDialog::setAntialiasText(bool enable) 0972 { 0973 preview(Profile::AntiAliasFonts, enable); 0974 updateTempProfileProperty(Profile::AntiAliasFonts, enable); 0975 0976 const QFont font = _profile->font(); 0977 updateFontPreview(font); 0978 } 0979 0980 void EditProfileDialog::toggleAllowLinkEscapeSequence(bool enable) 0981 { 0982 updateTempProfileProperty(Profile::AllowEscapedLinks, enable); 0983 } 0984 0985 void EditProfileDialog::linkEscapeSequenceTextsChanged() 0986 { 0987 updateTempProfileProperty(Profile::EscapedLinksSchema, _mouseUi->linkEscapeSequenceTexts->text()); 0988 } 0989 void EditProfileDialog::setVerticalLine(bool value) 0990 { 0991 updateTempProfileProperty(Profile::VerticalLine, value); 0992 } 0993 0994 void EditProfileDialog::setVerticalLineColumn(int value) 0995 { 0996 updateTempProfileProperty(Profile::VerticalLineAtChar, value); 0997 } 0998 0999 void EditProfileDialog::setBoldIntense(bool enable) 1000 { 1001 preview(Profile::BoldIntense, enable); 1002 updateTempProfileProperty(Profile::BoldIntense, enable); 1003 } 1004 1005 void EditProfileDialog::useFontLineCharacters(bool enable) 1006 { 1007 preview(Profile::UseFontLineCharacters, enable); 1008 updateTempProfileProperty(Profile::UseFontLineCharacters, enable); 1009 } 1010 1011 void EditProfileDialog::toggleBlinkingCursor(bool enable) 1012 { 1013 preview(Profile::BlinkingCursorEnabled, enable); 1014 updateTempProfileProperty(Profile::BlinkingCursorEnabled, enable); 1015 } 1016 1017 void EditProfileDialog::setCursorShape(int index) 1018 { 1019 preview(Profile::CursorShape, index); 1020 updateTempProfileProperty(Profile::CursorShape, index); 1021 } 1022 1023 void EditProfileDialog::autoCursorColor() 1024 { 1025 preview(Profile::UseCustomCursorColor, false); 1026 updateTempProfileProperty(Profile::UseCustomCursorColor, false); 1027 } 1028 1029 void EditProfileDialog::customCursorColor() 1030 { 1031 preview(Profile::UseCustomCursorColor, true); 1032 updateTempProfileProperty(Profile::UseCustomCursorColor, true); 1033 } 1034 1035 void EditProfileDialog::customCursorColorChanged(const QColor &color) 1036 { 1037 preview(Profile::CustomCursorColor, color); 1038 updateTempProfileProperty(Profile::CustomCursorColor, color); 1039 1040 // ensure that custom cursor colors are enabled 1041 _appearanceUi->customCursorColorButton->click(); 1042 } 1043 1044 void EditProfileDialog::customCursorTextColorChanged(const QColor &color) 1045 { 1046 preview(Profile::CustomCursorTextColor, color); 1047 updateTempProfileProperty(Profile::CustomCursorTextColor, color); 1048 1049 // ensure that custom cursor colors are enabled 1050 _appearanceUi->customCursorColorButton->click(); 1051 } 1052 1053 void EditProfileDialog::terminalMarginChanged(int margin) 1054 { 1055 preview(Profile::TerminalMargin, margin); 1056 updateTempProfileProperty(Profile::TerminalMargin, margin); 1057 } 1058 1059 void EditProfileDialog::lineSpacingChanged(int spacing) 1060 { 1061 preview(Profile::LineSpacing, spacing); 1062 updateTempProfileProperty(Profile::LineSpacing, spacing); 1063 } 1064 1065 void EditProfileDialog::setTerminalCenter(bool enable) 1066 { 1067 preview(Profile::TerminalCenter, enable); 1068 updateTempProfileProperty(Profile::TerminalCenter, enable); 1069 } 1070 1071 void EditProfileDialog::toggleMouseWheelZoom(bool enable) 1072 { 1073 updateTempProfileProperty(Profile::MouseWheelZoomEnabled, enable); 1074 } 1075 1076 void EditProfileDialog::toggleAlternateScrolling(bool enable) 1077 { 1078 updateTempProfileProperty(Profile::AlternateScrolling, enable); 1079 } 1080 1081 void EditProfileDialog::toggleAllowColorFilter(bool enable) 1082 { 1083 updateTempProfileProperty(Profile::ColorFilterEnabled, enable); 1084 } 1085 1086 void EditProfileDialog::toggleAllowMouseTracking(bool allow) 1087 { 1088 updateTempProfileProperty(Profile::AllowMouseTracking, allow); 1089 } 1090 1091 void EditProfileDialog::updateColorSchemeList(const QString &selectedColorSchemeName) 1092 { 1093 if (_appearanceUi->colorSchemeList->model() == nullptr) { 1094 _appearanceUi->colorSchemeList->setModel(new QStandardItemModel(this)); 1095 } 1096 1097 std::shared_ptr<const ColorScheme> selectedColorScheme = ColorSchemeManager::instance()->findColorScheme(selectedColorSchemeName); 1098 1099 auto *model = qobject_cast<QStandardItemModel *>(_appearanceUi->colorSchemeList->model()); 1100 1101 Q_ASSERT(model); 1102 1103 model->clear(); 1104 1105 QStandardItem *selectedItem = nullptr; 1106 1107 const QList<std::shared_ptr<const ColorScheme>> schemeList = ColorSchemeManager::instance()->allColorSchemes(); 1108 1109 for (const std::shared_ptr<const ColorScheme> &scheme : schemeList) { 1110 QStandardItem *item = new QStandardItem(scheme->description()); 1111 item->setData(QVariant::fromValue(scheme), Qt::UserRole + 1); 1112 item->setData(QVariant::fromValue(_profile->font()), Qt::UserRole + 2); 1113 item->setFlags(item->flags()); 1114 1115 // if selectedColorSchemeName is not empty then select that scheme 1116 // after saving the changes in the colorScheme editor 1117 if (selectedColorScheme == scheme) { 1118 selectedItem = item; 1119 } 1120 1121 model->appendRow(item); 1122 } 1123 1124 model->sort(0); 1125 1126 if (selectedItem != nullptr) { 1127 _appearanceUi->colorSchemeList->updateGeometry(); 1128 _appearanceUi->colorSchemeList->selectionModel()->setCurrentIndex(selectedItem->index(), QItemSelectionModel::Select); 1129 1130 // update transparency warning label 1131 updateTransparencyWarning(); 1132 } 1133 } 1134 1135 void EditProfileDialog::updateKeyBindingsList(const QString &selectKeyBindingsName) 1136 { 1137 if (_keyboardUi->keyBindingList->model() == nullptr) { 1138 _keyboardUi->keyBindingList->setModel(new QStandardItemModel(this)); 1139 } 1140 1141 auto *model = qobject_cast<QStandardItemModel *>(_keyboardUi->keyBindingList->model()); 1142 1143 Q_ASSERT(model); 1144 1145 model->clear(); 1146 1147 QStandardItem *selectedItem = nullptr; 1148 1149 const QStringList &translatorNames = _keyManager->allTranslators(); 1150 for (const QString &translatorName : translatorNames) { 1151 const KeyboardTranslator *translator = _keyManager->findTranslator(translatorName); 1152 if (translator == nullptr) { 1153 continue; 1154 } 1155 1156 QStandardItem *item = new QStandardItem(translator->description()); 1157 item->setEditable(false); 1158 item->setData(QVariant::fromValue(translator), Qt::UserRole + 1); 1159 item->setData(QVariant::fromValue(_keyManager->findTranslatorPath(translatorName)), Qt::ToolTipRole); 1160 item->setData(QVariant::fromValue(_profile->font()), Qt::UserRole + 2); 1161 item->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-keyboard"))); 1162 1163 if (selectKeyBindingsName == translatorName) { 1164 selectedItem = item; 1165 } 1166 1167 model->appendRow(item); 1168 } 1169 1170 model->sort(0); 1171 1172 if (selectedItem != nullptr) { 1173 _keyboardUi->keyBindingList->selectionModel()->setCurrentIndex(selectedItem->index(), QItemSelectionModel::Select); 1174 } 1175 } 1176 1177 QSize EditProfileDialog::sizeHint() const 1178 { 1179 QFontMetrics fm(font()); 1180 const int ch = fm.boundingRect(QLatin1Char('0')).width(); 1181 1182 // By default minimum size is used. Increase it to make text inputs 1183 // on "tabs" page wider and to add some whitespace on right side 1184 // of other pages. The window will not be wider than 2/3 of 1185 // the screen width (unless necessary to fit everything) 1186 return QDialog::sizeHint() + QSize(10 * ch, 0); 1187 } 1188 1189 void EditProfileDialog::unpreviewAll() 1190 { 1191 // undo any preview changes 1192 if (!_previewedProperties.empty()) { 1193 ProfileManager::instance()->changeProfile(_profile, _previewedProperties, false); 1194 } 1195 } 1196 1197 void EditProfileDialog::unpreview(Profile::Property prop) 1198 { 1199 auto node = _previewedProperties.extract(prop); 1200 if (!node) { 1201 return; 1202 } 1203 1204 Profile::PropertyMap map; 1205 map.insert(std::move(node)); 1206 ProfileManager::instance()->changeProfile(_profile, map, false); 1207 } 1208 1209 void EditProfileDialog::preview(Profile::Property prop, const QVariant &value) 1210 { 1211 const Profile::Ptr original = _profile; 1212 1213 // skip previews for profile groups if the profiles in the group 1214 // have conflicting original values for the property 1215 // 1216 // TODO - Save the original values for each profile and use to unpreview properties 1217 ProfileGroup::Ptr group = original->asGroup(); 1218 if (group && group->profiles().count() > 1 && original->property<QVariant>(prop).isNull()) { 1219 return; 1220 } 1221 1222 _previewedProperties.insert({prop, original->property<QVariant>(prop)}); 1223 1224 // temporary change to color scheme 1225 ProfileManager::instance()->changeProfile(_profile, {{prop, value}}, false); 1226 } 1227 1228 void EditProfileDialog::previewColorScheme(const QModelIndex &index) 1229 { 1230 const QString &name = index.data(Qt::UserRole + 1).value<std::shared_ptr<const ColorScheme>>()->name(); 1231 preview(Profile::ColorScheme, name); 1232 } 1233 1234 void EditProfileDialog::showFontDialog() 1235 { 1236 if (_fontDialog == nullptr) { 1237 _fontDialog = new FontDialog(this); 1238 _fontDialog->setModal(true); 1239 connect(_fontDialog, &FontDialog::fontChanged, this, [this](const QFont &font) { 1240 preview(Profile::Font, font); 1241 updateFontPreview(font); 1242 }); 1243 connect(_fontDialog, &FontDialog::accepted, this, [this]() { 1244 const QFont font = _fontDialog->font(); 1245 preview(Profile::Font, font); 1246 updateTempProfileProperty(Profile::Font, font); 1247 updateFontPreview(font); 1248 }); 1249 connect(_fontDialog, &FontDialog::rejected, this, [this]() { 1250 unpreview(Profile::Font); 1251 updateFontPreview(_profile->font()); 1252 }); 1253 } 1254 _fontDialog->setFont(_profile->font()); 1255 _fontDialog->show(); 1256 } 1257 1258 void EditProfileDialog::showEmojiFontDialog() 1259 { 1260 if (_emojiFontDialog == nullptr) { 1261 _emojiFontDialog = new FontDialog(this, true, _profile->emojiFont()); 1262 _emojiFontDialog->setModal(true); 1263 connect(_emojiFontDialog, &FontDialog::fontChanged, this, [this](const QFont &font) { 1264 preview(Profile::EmojiFont, font); 1265 updateEmojiFontPreview(font); 1266 }); 1267 connect(_emojiFontDialog, &FontDialog::accepted, this, [this]() { 1268 const QFont font = _emojiFontDialog->font(); 1269 preview(Profile::EmojiFont, font); 1270 updateTempProfileProperty(Profile::EmojiFont, font); 1271 updateEmojiFontPreview(font); 1272 }); 1273 connect(_emojiFontDialog, &FontDialog::rejected, this, [this]() { 1274 unpreview(Profile::EmojiFont); 1275 updateEmojiFontPreview(_profile->emojiFont()); 1276 }); 1277 } 1278 _emojiFontDialog->show(); 1279 } 1280 1281 void EditProfileDialog::updateFontPreview(QFont font) 1282 { 1283 bool aa = _profile->antiAliasFonts(); 1284 font.setStyleStrategy(aa ? QFont::PreferAntialias : QFont::NoAntialias); 1285 1286 _appearanceUi->fontPreview->setFont(font); 1287 _appearanceUi->fontPreview->setText(QStringLiteral("%1 %2pt").arg(font.family()).arg(font.pointSize())); 1288 } 1289 1290 void EditProfileDialog::updateEmojiFontPreview(QFont font) 1291 { 1292 bool aa = _profile->antiAliasFonts(); 1293 font.setStyleStrategy(aa ? QFont::PreferAntialias : QFont::NoAntialias); 1294 QFont emojiFont = _profile->emojiFont(); 1295 _appearanceUi->emojiFontPreview->setFont(_profile->font()); 1296 _appearanceUi->emojiFontPreview->setText(QStringLiteral("%1 %2pt").arg(emojiFont.family()).arg(emojiFont.pointSize())); 1297 } 1298 1299 void EditProfileDialog::removeColorScheme() 1300 { 1301 const QModelIndexList selected = _appearanceUi->colorSchemeList->selectionModel()->selectedIndexes(); 1302 if (selected.isEmpty()) { 1303 return; 1304 } 1305 const QString &name = selected.first().data(Qt::UserRole + 1).value<std::shared_ptr<const ColorScheme>>()->name(); 1306 Q_ASSERT(!name.isEmpty()); 1307 if (ColorSchemeManager::instance()->deleteColorScheme(name)) { 1308 _appearanceUi->colorSchemeList->model()->removeRow(selected.first().row()); 1309 } 1310 } 1311 1312 void EditProfileDialog::gotNewColorSchemes(const QList<KNSCore::Entry> &changedEntries) 1313 { 1314 int failures = 0; 1315 for (auto &entry : std::as_const(changedEntries)) { 1316 switch (entry.status()) { 1317 case KNSCore::Entry::Installed: 1318 for (const QString &file : entry.installedFiles()) { 1319 if (ColorSchemeManager::instance()->loadColorScheme(file)) { 1320 continue; 1321 } 1322 qWarning() << "Failed to load file" << file; 1323 ++failures; 1324 } 1325 if (failures == entry.installedFiles().size()) { 1326 _appearanceUi->colorSchemeMessageWidget->setText(xi18nc("@info", "Scheme <resource>%1</resource> failed to load.", entry.name())); 1327 _appearanceUi->colorSchemeMessageWidget->animatedShow(); 1328 QTimer::singleShot(8000, _appearanceUi->colorSchemeMessageWidget, &KMessageWidget::animatedHide); 1329 } 1330 break; 1331 case KNSCore::Entry::Deleted: 1332 for (const auto &file : entry.uninstalledFiles()) { 1333 if (ColorSchemeManager::instance()->unloadColorScheme(file)) { 1334 continue; 1335 } 1336 qWarning() << "Failed to unload file" << file; 1337 // If unloading fails we do not care. If the scheme failed here 1338 // it either wasn't loaded or was invalid to begin with. 1339 } 1340 break; 1341 case KNSCore::Entry::Invalid: 1342 case KNSCore::Entry::Installing: 1343 case KNSCore::Entry::Downloadable: 1344 case KNSCore::Entry::Updateable: 1345 case KNSCore::Entry::Updating: 1346 // Not interesting. 1347 break; 1348 } 1349 } 1350 updateColorSchemeList(currentColorSchemeName()); 1351 } 1352 1353 void EditProfileDialog::resetColorScheme() 1354 { 1355 QModelIndexList selected = _appearanceUi->colorSchemeList->selectionModel()->selectedIndexes(); 1356 1357 if (!selected.isEmpty()) { 1358 const QString &name = selected.first().data(Qt::UserRole + 1).value<std::shared_ptr<const ColorScheme>>()->name(); 1359 1360 ColorSchemeManager::instance()->deleteColorScheme(name); 1361 1362 // select the colorScheme used in the current profile 1363 updateColorSchemeList(currentColorSchemeName()); 1364 } 1365 } 1366 1367 void EditProfileDialog::showColorSchemeEditor(bool isNewScheme) 1368 { 1369 // Finding selected ColorScheme 1370 QModelIndexList selected = _appearanceUi->colorSchemeList->selectionModel()->selectedIndexes(); 1371 QAbstractItemModel *model = _appearanceUi->colorSchemeList->model(); 1372 std::shared_ptr<const ColorScheme> colors; 1373 if (!selected.isEmpty()) { 1374 colors = model->data(selected.first(), Qt::UserRole + 1).value<std::shared_ptr<const ColorScheme>>(); 1375 } else { 1376 colors = ColorSchemeManager::instance()->defaultColorScheme(); 1377 } 1378 1379 Q_ASSERT(colors); 1380 1381 // Setting up ColorSchemeEditor ui 1382 // close any running ColorSchemeEditor 1383 if (_colorDialog != nullptr) { 1384 closeColorSchemeEditor(); 1385 } 1386 _colorDialog = new ColorSchemeEditor(WindowSystemInfo::compositingActive(), this); 1387 1388 connect(_colorDialog, &Konsole::ColorSchemeEditor::colorSchemeSaveRequested, this, &Konsole::EditProfileDialog::saveColorScheme); 1389 _colorDialog->setup(colors, isNewScheme); 1390 1391 _colorDialog->show(); 1392 } 1393 1394 void EditProfileDialog::closeColorSchemeEditor() 1395 { 1396 if (_colorDialog != nullptr) { 1397 _colorDialog->close(); 1398 delete _colorDialog; 1399 } 1400 } 1401 1402 void EditProfileDialog::newColorScheme() 1403 { 1404 showColorSchemeEditor(true); 1405 } 1406 1407 void EditProfileDialog::editColorScheme() 1408 { 1409 showColorSchemeEditor(false); 1410 } 1411 1412 void EditProfileDialog::saveColorScheme(const ColorScheme &scheme, bool isNewScheme) 1413 { 1414 std::shared_ptr<ColorScheme> newScheme = std::make_shared<ColorScheme>(scheme); 1415 1416 // if this is a new color scheme, pick a name based on the description 1417 if (isNewScheme) { 1418 newScheme->setName(newScheme->description()); 1419 } 1420 1421 ColorSchemeManager::instance()->addColorScheme(newScheme); 1422 1423 const QString &selectedColorSchemeName = newScheme->name(); 1424 1425 // select the edited or the new colorScheme after saving the changes 1426 updateColorSchemeList(selectedColorSchemeName); 1427 1428 preview(Profile::ColorScheme, newScheme->name()); 1429 } 1430 1431 void EditProfileDialog::colorSchemeSelected() 1432 { 1433 QModelIndexList selected = _appearanceUi->colorSchemeList->selectionModel()->selectedIndexes(); 1434 1435 if (!selected.isEmpty()) { 1436 QAbstractItemModel *model = _appearanceUi->colorSchemeList->model(); 1437 std::shared_ptr<const ColorScheme> colors = model->data(selected.first(), Qt::UserRole + 1).value<std::shared_ptr<const ColorScheme>>(); 1438 if (colors != nullptr) { 1439 updateTempProfileProperty(Profile::ColorScheme, colors->name()); 1440 previewColorScheme(selected.first()); 1441 1442 updateTransparencyWarning(); 1443 } 1444 } 1445 1446 updateColorSchemeButtons(); 1447 } 1448 1449 void EditProfileDialog::updateColorSchemeButtons() 1450 { 1451 enableIfNonEmptySelection(_appearanceUi->editColorSchemeButton, _appearanceUi->colorSchemeList->selectionModel()); 1452 1453 QModelIndexList selected = _appearanceUi->colorSchemeList->selectionModel()->selectedIndexes(); 1454 1455 if (!selected.isEmpty()) { 1456 const QString &name = selected.first().data(Qt::UserRole + 1).value<std::shared_ptr<const ColorScheme>>()->name(); 1457 1458 bool isResettable = ColorSchemeManager::instance()->canResetColorScheme(name); 1459 _appearanceUi->resetColorSchemeButton->setEnabled(isResettable); 1460 1461 bool isDeletable = ColorSchemeManager::instance()->isColorSchemeDeletable(name); 1462 // if a colorScheme can be restored then it can't be deleted 1463 _appearanceUi->removeColorSchemeButton->setEnabled(isDeletable && !isResettable); 1464 } else { 1465 _appearanceUi->removeColorSchemeButton->setEnabled(false); 1466 _appearanceUi->resetColorSchemeButton->setEnabled(false); 1467 } 1468 } 1469 1470 void EditProfileDialog::updateKeyBindingsButtons() 1471 { 1472 QModelIndexList selected = _keyboardUi->keyBindingList->selectionModel()->selectedIndexes(); 1473 1474 if (!selected.isEmpty()) { 1475 _keyboardUi->editKeyBindingsButton->setEnabled(true); 1476 1477 const QString &name = selected.first().data(Qt::UserRole + 1).value<const KeyboardTranslator *>()->name(); 1478 1479 bool isResettable = _keyManager->isTranslatorResettable(name); 1480 _keyboardUi->resetKeyBindingsButton->setEnabled(isResettable); 1481 1482 bool isDeletable = _keyManager->isTranslatorDeletable(name); 1483 1484 // if a key bindings scheme can be reset then it can't be deleted 1485 _keyboardUi->removeKeyBindingsButton->setEnabled(isDeletable && !isResettable); 1486 } 1487 } 1488 1489 void EditProfileDialog::enableIfNonEmptySelection(QWidget *widget, QItemSelectionModel *selectionModel) 1490 { 1491 widget->setEnabled(selectionModel->hasSelection()); 1492 } 1493 1494 void EditProfileDialog::updateTransparencyWarning() 1495 { 1496 // zero or one indexes can be selected 1497 const QModelIndexList selected = _appearanceUi->colorSchemeList->selectionModel()->selectedIndexes(); 1498 for (const QModelIndex &index : selected) { 1499 bool needTransparency = index.data(Qt::UserRole + 1).value<std::shared_ptr<const ColorScheme>>()->opacity() < 1.0; 1500 1501 if (!needTransparency) { 1502 _appearanceUi->transparencyWarningWidget->setHidden(true); 1503 } else if (!WindowSystemInfo::compositingActive()) { 1504 _appearanceUi->transparencyWarningWidget->setText( 1505 i18n("This color scheme uses a transparent background" 1506 " which does not appear to be supported on your" 1507 " desktop")); 1508 _appearanceUi->transparencyWarningWidget->setHidden(false); 1509 } else if (!WindowSystemInfo::HAVE_TRANSPARENCY) { 1510 _appearanceUi->transparencyWarningWidget->setText( 1511 i18n("Konsole was started before desktop effects were enabled." 1512 " You need to restart Konsole to see transparent background.")); 1513 _appearanceUi->transparencyWarningWidget->setHidden(false); 1514 } 1515 } 1516 } 1517 1518 void EditProfileDialog::resetTempProfile() 1519 { 1520 _tempProfile->clear(); 1521 _tempProfile->setHidden(true); 1522 } 1523 1524 void EditProfileDialog::updateTempProfileProperty(Profile::Property property, const QVariant &value) 1525 { 1526 _tempProfile->setProperty(property, value); 1527 updateButtonApply(); 1528 } 1529 1530 void EditProfileDialog::updateButtonApply() 1531 { 1532 auto filterFunc = [this](const auto &keyValue) { 1533 const auto &[property, value] = keyValue; 1534 // for previewed property 1535 auto it = _previewedProperties.find(property); 1536 if (it != _previewedProperties.cend()) { 1537 if (value != it->second) { 1538 return true; 1539 } 1540 // for not-previewed property 1541 // 1542 // for the Profile::KeyBindings property, if it's set in the _tempProfile 1543 // then the user opened the edit key bindings dialog and clicked 1544 // OK, and could have add/removed a key bindings rule 1545 } else if (property == Profile::KeyBindings // 1546 || value != _profile->property<QVariant>(property)) { 1547 return true; 1548 } 1549 1550 return false; 1551 }; 1552 1553 const Profile::PropertyMap &map = _tempProfile->properties(); 1554 bool userModified = std::any_of(map.cbegin(), map.cend(), filterFunc); 1555 1556 if (_generalUi->setAsDefaultButton->isChecked() != _isDefault) { 1557 userModified = true; 1558 } 1559 1560 _buttonBox->button(QDialogButtonBox::Apply)->setEnabled(userModified); 1561 } 1562 1563 void EditProfileDialog::setupKeyboardPage(const Profile::Ptr & /* profile */) 1564 { 1565 // setup translator list 1566 updateKeyBindingsList(_profile->keyBindings()); 1567 1568 connect(_keyboardUi->keyBindingList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Konsole::EditProfileDialog::keyBindingSelected); 1569 connect(_keyboardUi->newKeyBindingsButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::newKeyBinding); 1570 1571 _keyboardUi->editKeyBindingsButton->setIcon(QIcon::fromTheme(QStringLiteral("document-edit"))); 1572 _keyboardUi->removeKeyBindingsButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); 1573 _keyboardUi->newKeyBindingsButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); 1574 _keyboardUi->resetKeyBindingsButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo"))); 1575 1576 _keyboardUi->editKeyBindingsButton->setEnabled(false); 1577 _keyboardUi->removeKeyBindingsButton->setEnabled(false); 1578 _keyboardUi->resetKeyBindingsButton->setEnabled(false); 1579 1580 updateKeyBindingsButtons(); 1581 1582 connect(_keyboardUi->editKeyBindingsButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::editKeyBinding); 1583 connect(_keyboardUi->removeKeyBindingsButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::removeKeyBinding); 1584 connect(_keyboardUi->resetKeyBindingsButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::resetKeyBindings); 1585 } 1586 1587 void EditProfileDialog::keyBindingSelected() 1588 { 1589 QModelIndexList selected = _keyboardUi->keyBindingList->selectionModel()->selectedIndexes(); 1590 1591 if (!selected.isEmpty()) { 1592 QAbstractItemModel *model = _keyboardUi->keyBindingList->model(); 1593 const auto *translator = model->data(selected.first(), Qt::UserRole + 1).value<const KeyboardTranslator *>(); 1594 if (translator != nullptr) { 1595 updateTempProfileProperty(Profile::KeyBindings, translator->name()); 1596 } 1597 } 1598 1599 updateKeyBindingsButtons(); 1600 } 1601 1602 void EditProfileDialog::removeKeyBinding() 1603 { 1604 QModelIndexList selected = _keyboardUi->keyBindingList->selectionModel()->selectedIndexes(); 1605 1606 if (!selected.isEmpty()) { 1607 const QString &name = selected.first().data(Qt::UserRole + 1).value<const KeyboardTranslator *>()->name(); 1608 if (KeyboardTranslatorManager::instance()->deleteTranslator(name)) { 1609 _keyboardUi->keyBindingList->model()->removeRow(selected.first().row()); 1610 } 1611 } 1612 } 1613 1614 void EditProfileDialog::showKeyBindingEditor(bool isNewTranslator) 1615 { 1616 QModelIndexList selected = _keyboardUi->keyBindingList->selectionModel()->selectedIndexes(); 1617 QAbstractItemModel *model = _keyboardUi->keyBindingList->model(); 1618 1619 const KeyboardTranslator *translator = nullptr; 1620 if (!selected.isEmpty()) { 1621 translator = model->data(selected.first(), Qt::UserRole + 1).value<const KeyboardTranslator *>(); 1622 } else { 1623 translator = _keyManager->defaultTranslator(); 1624 } 1625 1626 auto *editor = new KeyBindingEditor(this); 1627 editor->setAttribute(Qt::WA_DeleteOnClose); 1628 editor->setModal(true); 1629 1630 if (translator != nullptr) { 1631 editor->setup(translator, _profile->keyBindings(), isNewTranslator); 1632 } 1633 1634 connect(editor, &Konsole::KeyBindingEditor::updateKeyBindingsListRequest, this, &Konsole::EditProfileDialog::updateKeyBindingsList); 1635 connect(editor, &Konsole::KeyBindingEditor::updateTempProfileKeyBindingsRequest, this, &Konsole::EditProfileDialog::updateTempProfileProperty); 1636 1637 editor->show(); 1638 } 1639 1640 void EditProfileDialog::newKeyBinding() 1641 { 1642 showKeyBindingEditor(true); 1643 } 1644 1645 void EditProfileDialog::editKeyBinding() 1646 { 1647 showKeyBindingEditor(false); 1648 } 1649 1650 void EditProfileDialog::resetKeyBindings() 1651 { 1652 QModelIndexList selected = _keyboardUi->keyBindingList->selectionModel()->selectedIndexes(); 1653 1654 if (!selected.isEmpty()) { 1655 const QString &name = selected.first().data(Qt::UserRole + 1).value<const KeyboardTranslator *>()->name(); 1656 1657 _keyManager->deleteTranslator(name); 1658 // find and load the translator 1659 _keyManager->findTranslator(name); 1660 1661 updateKeyBindingsList(name); 1662 } 1663 } 1664 1665 void EditProfileDialog::setupButtonGroup(const ButtonGroupOptions &options, const Profile::Ptr &profile) 1666 { 1667 auto currentValue = profile->property<int>(options.profileProperty); 1668 1669 for (auto option : options.buttons) { 1670 options.group->setId(option.button, option.value); 1671 } 1672 1673 Q_ASSERT(options.buttons.count() > 0); 1674 auto *activeButton = options.group->button(currentValue); 1675 if (activeButton == nullptr) { 1676 activeButton = options.buttons[0].button; 1677 } 1678 activeButton->setChecked(true); 1679 connect(options.group, &QButtonGroup::idClicked, this, [this, options](int value) { 1680 if (options.preview) { 1681 preview(options.profileProperty, value); 1682 } 1683 updateTempProfileProperty(options.profileProperty, value); 1684 }); 1685 } 1686 1687 void EditProfileDialog::setupScrollingPage(const Profile::Ptr &profile) 1688 { 1689 // setup scrollbar radio 1690 const ButtonGroupOptions scrollBarPositionOptions = { 1691 _scrollingUi->scrollBarPosition, // group 1692 Profile::ScrollBarPosition, // profileProperty 1693 false, // preview 1694 { 1695 // buttons 1696 {_scrollingUi->scrollBarRightButton, Enum::ScrollBarRight}, 1697 {_scrollingUi->scrollBarLeftButton, Enum::ScrollBarLeft}, 1698 {_scrollingUi->scrollBarHiddenButton, Enum::ScrollBarHidden}, 1699 }, 1700 }; 1701 setupButtonGroup(scrollBarPositionOptions, profile); 1702 1703 // setup scrollback type radio 1704 auto scrollBackType = profile->property<int>(Profile::HistoryMode); 1705 _scrollingUi->historySizeWidget->setMode(Enum::HistoryModeEnum(scrollBackType)); 1706 connect(_scrollingUi->historySizeWidget, &Konsole::HistorySizeWidget::historyModeChanged, this, &Konsole::EditProfileDialog::historyModeChanged); 1707 1708 // setup scrollback line count spinner 1709 const int historySize = profile->historySize(); 1710 _scrollingUi->historySizeWidget->setLineCount(historySize); 1711 1712 // setup scrollpageamount type radio 1713 auto scrollFullPage = profile->property<int>(Profile::ScrollFullPage); 1714 1715 _scrollingUi->scrollHalfPage->setChecked(Enum::ScrollPageHalf == scrollFullPage); 1716 connect(_scrollingUi->scrollHalfPage, &QPushButton::clicked, this, &EditProfileDialog::scrollHalfPage); 1717 1718 _scrollingUi->scrollFullPage->setChecked(Enum::ScrollPageFull == scrollFullPage); 1719 connect(_scrollingUi->scrollFullPage, &QPushButton::clicked, this, &EditProfileDialog::scrollFullPage); 1720 1721 _scrollingUi->highlightScrolledLinesButton->setChecked(profile->property<bool>(Profile::HighlightScrolledLines)); 1722 connect(_scrollingUi->highlightScrolledLinesButton, &QPushButton::clicked, this, &EditProfileDialog::toggleHighlightScrolledLines); 1723 1724 _scrollingUi->reflowLinesButton->setChecked(profile->property<bool>(Profile::ReflowLines)); 1725 connect(_scrollingUi->reflowLinesButton, &QPushButton::clicked, this, &EditProfileDialog::toggleReflowLines); 1726 1727 // signals and slots 1728 connect(_scrollingUi->historySizeWidget, &Konsole::HistorySizeWidget::historySizeChanged, this, &Konsole::EditProfileDialog::historySizeChanged); 1729 } 1730 1731 void EditProfileDialog::historySizeChanged(int lineCount) 1732 { 1733 updateTempProfileProperty(Profile::HistorySize, lineCount); 1734 } 1735 1736 void EditProfileDialog::historyModeChanged(Enum::HistoryModeEnum mode) 1737 { 1738 updateTempProfileProperty(Profile::HistoryMode, mode); 1739 } 1740 1741 void EditProfileDialog::scrollFullPage() 1742 { 1743 updateTempProfileProperty(Profile::ScrollFullPage, Enum::ScrollPageFull); 1744 } 1745 1746 void EditProfileDialog::scrollHalfPage() 1747 { 1748 updateTempProfileProperty(Profile::ScrollFullPage, Enum::ScrollPageHalf); 1749 } 1750 1751 void EditProfileDialog::toggleHighlightScrolledLines(bool enable) 1752 { 1753 updateTempProfileProperty(Profile::HighlightScrolledLines, enable); 1754 } 1755 1756 void EditProfileDialog::toggleReflowLines(bool enable) 1757 { 1758 updateTempProfileProperty(Profile::ReflowLines, enable); 1759 } 1760 1761 void EditProfileDialog::setupMousePage(const Profile::Ptr &profile) 1762 { 1763 _mouseUi->underlineLinksButton->setChecked(profile->property<bool>(Profile::UnderlineLinksEnabled)); 1764 connect(_mouseUi->underlineLinksButton, &QPushButton::toggled, this, &EditProfileDialog::toggleUnderlineLinks); 1765 _mouseUi->underlineFilesButton->setChecked(profile->property<bool>(Profile::UnderlineFilesEnabled)); 1766 connect(_mouseUi->underlineFilesButton, &QPushButton::toggled, this, &EditProfileDialog::toggleUnderlineFiles); 1767 _mouseUi->ctrlRequiredForDragButton->setChecked(profile->property<bool>(Profile::CtrlRequiredForDrag)); 1768 connect(_mouseUi->ctrlRequiredForDragButton, &QPushButton::toggled, this, &EditProfileDialog::toggleCtrlRequiredForDrag); 1769 _mouseUi->copyTextAsHTMLButton->setChecked(profile->property<bool>(Profile::CopyTextAsHTML)); 1770 connect(_mouseUi->copyTextAsHTMLButton, &QPushButton::toggled, this, &EditProfileDialog::toggleCopyTextAsHTML); 1771 _mouseUi->copyTextToClipboardButton->setChecked(profile->property<bool>(Profile::AutoCopySelectedText)); 1772 connect(_mouseUi->copyTextToClipboardButton, &QPushButton::toggled, this, &EditProfileDialog::toggleCopyTextToClipboard); 1773 _mouseUi->trimLeadingSpacesButton->setChecked(profile->property<bool>(Profile::TrimLeadingSpacesInSelectedText)); 1774 connect(_mouseUi->trimLeadingSpacesButton, &QPushButton::toggled, this, &EditProfileDialog::toggleTrimLeadingSpacesInSelectedText); 1775 _mouseUi->trimTrailingSpacesButton->setChecked(profile->property<bool>(Profile::TrimTrailingSpacesInSelectedText)); 1776 connect(_mouseUi->trimTrailingSpacesButton, &QPushButton::toggled, this, &EditProfileDialog::toggleTrimTrailingSpacesInSelectedText); 1777 _mouseUi->openLinksByDirectClickButton->setChecked(profile->property<bool>(Profile::OpenLinksByDirectClickEnabled)); 1778 connect(_mouseUi->openLinksByDirectClickButton, &QPushButton::toggled, this, &EditProfileDialog::toggleOpenLinksByDirectClick); 1779 _mouseUi->dropUrlsAsText->setChecked(profile->property<bool>(Profile::DropUrlsAsText)); 1780 connect(_mouseUi->dropUrlsAsText, &QPushButton::toggled, this, &EditProfileDialog::toggleDropUrlsAsText); 1781 _mouseUi->enableAlternateScrollingButton->setChecked(profile->property<bool>(Profile::AlternateScrolling)); 1782 connect(_mouseUi->enableAlternateScrollingButton, &QPushButton::toggled, this, &EditProfileDialog::toggleAlternateScrolling); 1783 _mouseUi->allowColorFilters->setChecked(profile->property<bool>(Profile::ColorFilterEnabled)); 1784 connect(_mouseUi->allowColorFilters, &QPushButton::toggled, this, &EditProfileDialog::toggleAllowColorFilter); 1785 _mouseUi->allowMouseTrackingButton->setChecked(profile->property<bool>(Profile::AllowMouseTracking)); 1786 connect(_mouseUi->allowMouseTrackingButton, &QPushButton::toggled, this, &EditProfileDialog::toggleAllowMouseTracking); 1787 1788 // setup middle click paste mode 1789 const auto middleClickPasteMode = profile->property<int>(Profile::MiddleClickPasteMode); 1790 _mouseUi->pasteFromX11SelectionButton->setChecked(Enum::PasteFromX11Selection == middleClickPasteMode); 1791 connect(_mouseUi->pasteFromX11SelectionButton, &QPushButton::clicked, this, &EditProfileDialog::pasteFromX11Selection); 1792 _mouseUi->pasteFromClipboardButton->setChecked(Enum::PasteFromClipboard == middleClickPasteMode); 1793 connect(_mouseUi->pasteFromClipboardButton, &QPushButton::clicked, this, &EditProfileDialog::pasteFromClipboard); 1794 1795 _mouseUi->textEditorCustomBtn->setIcon(QIcon::fromTheme(QStringLiteral("document-edit"))); 1796 1797 // interaction options 1798 _mouseUi->wordCharacterEdit->setText(profile->wordCharacters()); 1799 1800 connect(_mouseUi->wordCharacterEdit, &QLineEdit::textChanged, this, &Konsole::EditProfileDialog::wordCharactersChanged); 1801 1802 const ButtonGroupOptions tripleClickModeOptions = { 1803 _mouseUi->tripleClickMode, // group 1804 Profile::TripleClickMode, // profileProperty 1805 false, // preview 1806 { 1807 // buttons 1808 {_mouseUi->tripleClickSelectsTheWholeLine, Enum::SelectWholeLine}, 1809 {_mouseUi->tripleClickSelectsFromMousePosition, Enum::SelectForwardsFromCursor}, 1810 }, 1811 }; 1812 setupButtonGroup(tripleClickModeOptions, profile); 1813 1814 _mouseUi->openLinksByDirectClickButton->setEnabled(_mouseUi->underlineLinksButton->isChecked() || _mouseUi->underlineFilesButton->isChecked()); 1815 1816 _mouseUi->enableMouseWheelZoomButton->setChecked(profile->mouseWheelZoomEnabled()); 1817 connect(_mouseUi->enableMouseWheelZoomButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::toggleMouseWheelZoom); 1818 1819 _mouseUi->allowLinkEscapeSequenceButton->setChecked(profile->allowEscapedLinks()); 1820 connect(_mouseUi->allowLinkEscapeSequenceButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::toggleAllowLinkEscapeSequence); 1821 1822 _mouseUi->linkEscapeSequenceTexts->setEnabled(profile->allowEscapedLinks()); 1823 _mouseUi->linkEscapeSequenceTexts->setText(profile->escapedLinksSchema().join(QLatin1Char(';'))); 1824 connect(_mouseUi->linkEscapeSequenceTexts, &QLineEdit::textChanged, this, &Konsole::EditProfileDialog::linkEscapeSequenceTextsChanged); 1825 1826 setTextEditorCombo(profile); 1827 } 1828 1829 void EditProfileDialog::setTextEditorCombo(const Profile::Ptr &profile) 1830 { 1831 static const Enum::TextEditorCmd editorsList[] = 1832 {Enum::Kate, Enum::KWrite, Enum::KDevelop, Enum::QtCreator, Enum::Gedit, Enum::gVim, Enum::CustomTextEditor}; 1833 1834 auto *editorCombo = _mouseUi->textEditorCombo; 1835 1836 QStandardItemModel *model = static_cast<QStandardItemModel *>(editorCombo->model()); 1837 Q_ASSERT(model); 1838 1839 for (auto editor : editorsList) { 1840 QString exec; 1841 QString displayName; 1842 QIcon icon; 1843 switch (editor) { 1844 case Enum::Kate: 1845 exec = QStringLiteral("kate"); 1846 displayName = QStringLiteral("Kate"); 1847 icon = QIcon::fromTheme(exec); 1848 break; 1849 case Enum::KWrite: 1850 exec = QStringLiteral("kwrite"); 1851 displayName = QStringLiteral("KWrite"); 1852 icon = QIcon::fromTheme(exec); 1853 break; 1854 case Enum::KDevelop: 1855 exec = QStringLiteral("kdevelop"); 1856 displayName = QStringLiteral("KDevelop"); 1857 icon = QIcon::fromTheme(exec); 1858 break; 1859 case Enum::QtCreator: 1860 exec = QStringLiteral("qtcreator"); 1861 displayName = QStringLiteral("Qt Creator"); 1862 icon = QIcon::fromTheme(exec); 1863 break; 1864 case Enum::Gedit: 1865 exec = QStringLiteral("gedit"); 1866 displayName = QStringLiteral("Gedit"); 1867 QIcon::fromTheme(QStringLiteral("org.gnome.gedit")); 1868 break; 1869 case Enum::gVim: 1870 exec = QStringLiteral("gvim"); 1871 displayName = QStringLiteral("gVim"); 1872 icon = QIcon::fromTheme(exec); 1873 break; 1874 case Enum::CustomTextEditor: 1875 displayName = QStringLiteral("Custom"); 1876 icon = QIcon::fromTheme(QStringLiteral("system-run")); 1877 break; 1878 default: 1879 break; 1880 } 1881 1882 editorCombo->addItem(icon, displayName); 1883 1884 // For "CustomTextEditor" we don't check if the binary exists 1885 const bool isAvailable = editor == Enum::CustomTextEditor || !QStandardPaths::findExecutable(exec).isEmpty(); 1886 // Make un-available editors look disabled in the combobox 1887 model->item(static_cast<int>(editor))->setEnabled(isAvailable); 1888 } 1889 1890 const auto currentEditor = profile->property<int>(Profile::TextEditorCmd); 1891 editorCombo->setCurrentIndex(currentEditor); 1892 1893 connect(editorCombo, &QComboBox::currentIndexChanged, this, [this](const int index) { 1894 updateTempProfileProperty(Profile::TextEditorCmd, index); 1895 _mouseUi->textEditorCustomBtn->setEnabled(index == Enum::CustomTextEditor); 1896 }); 1897 1898 _mouseUi->textEditorCustomBtn->setEnabled(currentEditor == Enum::CustomTextEditor); 1899 connect(_mouseUi->textEditorCustomBtn, &QAbstractButton::clicked, this, [this, profile]() { 1900 auto *dlg = new QInputDialog(static_cast<QWidget *>(this)); 1901 dlg->setLabelText( 1902 i18n("The format is e.g. 'editorExec PATH:LINE:COLUMN'\n\n" 1903 "PATH will be replaced by the path to the text file\n" 1904 "LINE will be replaced by the line number\n" 1905 "COLUMN (optional) will be replaced by the column number\n" 1906 "Note: you will need to replace 'PATH:LINE:COLUMN' by the actual\n" 1907 "syntax the editor you want to use supports; e.g.:\n" 1908 "gedit +LINE:COLUMN PATH\n\n" 1909 "If PATH or LINE aren't present in the command, this setting\n" 1910 "will be ignored and the file will be opened by the default text\n" 1911 "editor.")); 1912 const QString cmd = profile->customTextEditorCmd(); 1913 dlg->setTextValue(cmd); 1914 dlg->setAttribute(Qt::WA_DeleteOnClose); 1915 dlg->setWindowTitle(i18n("Text Editor Custom Command")); 1916 1917 QFontMetrics fm(font()); 1918 const int width = qMin(fm.averageCharWidth() * cmd.size(), this->width()); 1919 dlg->resize(width, dlg->height()); 1920 1921 connect(dlg, &QDialog::accepted, this, [this, dlg]() { 1922 updateTempProfileProperty(Profile::TextEditorCmdCustom, dlg->textValue()); 1923 }); 1924 1925 dlg->show(); 1926 }); 1927 } 1928 1929 void EditProfileDialog::setupAdvancedPage(const Profile::Ptr &profile) 1930 { 1931 _advancedUi->enableBlinkingTextButton->setChecked(profile->property<bool>(Profile::BlinkingTextEnabled)); 1932 connect(_advancedUi->enableBlinkingTextButton, &QPushButton::toggled, this, &EditProfileDialog::toggleBlinkingText); 1933 _advancedUi->enableFlowControlButton->setChecked(profile->property<bool>(Profile::FlowControlEnabled)); 1934 connect(_advancedUi->enableFlowControlButton, &QPushButton::toggled, this, &EditProfileDialog::toggleFlowControl); 1935 _appearanceUi->enableBlinkingCursorButton->setChecked(profile->property<bool>(Profile::BlinkingCursorEnabled)); 1936 connect(_appearanceUi->enableBlinkingCursorButton, &QPushButton::toggled, this, &EditProfileDialog::toggleBlinkingCursor); 1937 _advancedUi->enableReverseUrlHints->setChecked(profile->property<bool>(Profile::ReverseUrlHints)); 1938 connect(_advancedUi->enableReverseUrlHints, &QPushButton::toggled, this, &EditProfileDialog::toggleReverseUrlHints); 1939 1940 // Setup the URL hints modifier checkboxes 1941 { 1942 auto modifiers = profile->property<int>(Profile::UrlHintsModifiers); 1943 _advancedUi->urlHintsModifierShift->setChecked((modifiers & Qt::ShiftModifier) != 0U); 1944 _advancedUi->urlHintsModifierCtrl->setChecked((modifiers & Qt::ControlModifier) != 0U); 1945 _advancedUi->urlHintsModifierAlt->setChecked((modifiers & Qt::AltModifier) != 0U); 1946 _advancedUi->urlHintsModifierMeta->setChecked((modifiers & Qt::MetaModifier) != 0U); 1947 connect(_advancedUi->urlHintsModifierShift, &QCheckBox::toggled, this, &EditProfileDialog::updateUrlHintsModifier); 1948 connect(_advancedUi->urlHintsModifierCtrl, &QCheckBox::toggled, this, &EditProfileDialog::updateUrlHintsModifier); 1949 connect(_advancedUi->urlHintsModifierAlt, &QCheckBox::toggled, this, &EditProfileDialog::updateUrlHintsModifier); 1950 connect(_advancedUi->urlHintsModifierMeta, &QCheckBox::toggled, this, &EditProfileDialog::updateUrlHintsModifier); 1951 } 1952 1953 // encoding options 1954 auto codecAction = new KCodecAction(this); 1955 codecAction->setCurrentCodec(profile->defaultEncoding()); 1956 _advancedUi->selectEncodingButton->setMenu(codecAction->menu()); 1957 1958 connect(codecAction, &KCodecAction::codecNameTriggered, this, [this](const QByteArray &codecName) { 1959 setDefaultCodec(QTextCodec::codecForName(codecName)); 1960 }); 1961 1962 connect(codecAction, &KCodecAction::defaultItemTriggered, this, [this] { 1963 setDefaultCodec(QTextCodec::codecForLocale()); 1964 }); 1965 1966 _advancedUi->selectEncodingButton->setText(profile->defaultEncoding()); 1967 1968 _advancedUi->peekPrimaryWidget->setKeySequence(profile->peekPrimaryKeySequence()); 1969 connect(_advancedUi->peekPrimaryWidget, &QKeySequenceEdit::editingFinished, this, &EditProfileDialog::peekPrimaryKeySequenceChanged); 1970 } 1971 1972 int EditProfileDialog::maxSpinBoxWidth(const KPluralHandlingSpinBox *spinBox, const KLocalizedString &suffix) 1973 { 1974 static const int cursorWidth = 2; 1975 1976 const QFontMetrics fm(spinBox->fontMetrics()); 1977 const QString plural = suffix.subs(2).toString(); 1978 const QString singular = suffix.subs(1).toString(); 1979 const QString min = QString::number(spinBox->minimum()); 1980 const QString max = QString::number(spinBox->maximum()); 1981 const int pluralWidth = fm.boundingRect(plural).width(); 1982 const int singularWidth = fm.boundingRect(singular).width(); 1983 const int minWidth = fm.boundingRect(min).width(); 1984 const int maxWidth = fm.boundingRect(max).width(); 1985 const int width = qMax(pluralWidth, singularWidth) + qMax(minWidth, maxWidth) + cursorWidth; 1986 1987 // Based on QAbstractSpinBox::initStyleOption() from Qt 1988 QStyleOptionSpinBox opt; 1989 opt.initFrom(spinBox); 1990 opt.activeSubControls = QStyle::SC_None; 1991 opt.buttonSymbols = spinBox->buttonSymbols(); 1992 // Assume all spinboxes have buttons 1993 opt.subControls = QStyle::SC_SpinBoxFrame | QStyle::SC_SpinBoxEditField | QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown; 1994 opt.frame = spinBox->hasFrame(); 1995 1996 const QSize hint(width, spinBox->sizeHint().height()); 1997 const QSize spinBoxSize = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, spinBox); 1998 return spinBoxSize.width(); 1999 } 2000 2001 void EditProfileDialog::setDefaultCodec(QTextCodec *codec) 2002 { 2003 QString name = QString::fromLocal8Bit(codec->name()); 2004 2005 updateTempProfileProperty(Profile::DefaultEncoding, name); 2006 _advancedUi->selectEncodingButton->setText(name); 2007 } 2008 2009 void EditProfileDialog::wordCharactersChanged(const QString &text) 2010 { 2011 updateTempProfileProperty(Profile::WordCharacters, text); 2012 } 2013 2014 void EditProfileDialog::togglebidiRendering(bool enable) 2015 { 2016 updateTempProfileProperty(Profile::BidiRenderingEnabled, enable); 2017 _appearanceUi->enableBidiTableDirOverrideButton->setEnabled(enable); 2018 _appearanceUi->bidiLineLTR->setEnabled(enable); 2019 } 2020 2021 void EditProfileDialog::togglebidiTableDirOverride(bool enable) 2022 { 2023 updateTempProfileProperty(Profile::BidiTableDirOverride, enable); 2024 } 2025 2026 void EditProfileDialog::togglebidiLineLTR(bool enable) 2027 { 2028 updateTempProfileProperty(Profile::BidiLineLTR, enable); 2029 } 2030 2031 void EditProfileDialog::toggleUnderlineLinks(bool enable) 2032 { 2033 updateTempProfileProperty(Profile::UnderlineLinksEnabled, enable); 2034 2035 bool enableClick = _mouseUi->underlineFilesButton->isChecked() || enable; 2036 _mouseUi->openLinksByDirectClickButton->setEnabled(enableClick); 2037 } 2038 2039 void EditProfileDialog::toggleUnderlineFiles(bool enable) 2040 { 2041 updateTempProfileProperty(Profile::UnderlineFilesEnabled, enable); 2042 2043 bool enableClick = _mouseUi->underlineLinksButton->isChecked() || enable; 2044 _mouseUi->openLinksByDirectClickButton->setEnabled(enableClick); 2045 } 2046 2047 void EditProfileDialog::textEditorCmdEditLineChanged(const QString &text) 2048 { 2049 updateTempProfileProperty(Profile::TextEditorCmd, text); 2050 } 2051 2052 void EditProfileDialog::toggleCtrlRequiredForDrag(bool enable) 2053 { 2054 updateTempProfileProperty(Profile::CtrlRequiredForDrag, enable); 2055 } 2056 2057 void EditProfileDialog::toggleDropUrlsAsText(bool enable) 2058 { 2059 updateTempProfileProperty(Profile::DropUrlsAsText, enable); 2060 } 2061 2062 void EditProfileDialog::toggleOpenLinksByDirectClick(bool enable) 2063 { 2064 updateTempProfileProperty(Profile::OpenLinksByDirectClickEnabled, enable); 2065 } 2066 2067 void EditProfileDialog::toggleCopyTextAsHTML(bool enable) 2068 { 2069 updateTempProfileProperty(Profile::CopyTextAsHTML, enable); 2070 } 2071 2072 void EditProfileDialog::toggleCopyTextToClipboard(bool enable) 2073 { 2074 updateTempProfileProperty(Profile::AutoCopySelectedText, enable); 2075 } 2076 2077 void EditProfileDialog::toggleTrimLeadingSpacesInSelectedText(bool enable) 2078 { 2079 updateTempProfileProperty(Profile::TrimLeadingSpacesInSelectedText, enable); 2080 } 2081 2082 void EditProfileDialog::toggleTrimTrailingSpacesInSelectedText(bool enable) 2083 { 2084 updateTempProfileProperty(Profile::TrimTrailingSpacesInSelectedText, enable); 2085 } 2086 2087 void EditProfileDialog::pasteFromX11Selection() 2088 { 2089 updateTempProfileProperty(Profile::MiddleClickPasteMode, Enum::PasteFromX11Selection); 2090 } 2091 2092 void EditProfileDialog::pasteFromClipboard() 2093 { 2094 updateTempProfileProperty(Profile::MiddleClickPasteMode, Enum::PasteFromClipboard); 2095 } 2096 2097 void EditProfileDialog::TripleClickModeChanged(int newValue) 2098 { 2099 updateTempProfileProperty(Profile::TripleClickMode, newValue); 2100 } 2101 2102 void EditProfileDialog::updateUrlHintsModifier(bool) 2103 { 2104 Qt::KeyboardModifiers modifiers; 2105 if (_advancedUi->urlHintsModifierShift->isChecked()) { 2106 modifiers |= Qt::ShiftModifier; 2107 } 2108 if (_advancedUi->urlHintsModifierCtrl->isChecked()) { 2109 modifiers |= Qt::ControlModifier; 2110 } 2111 if (_advancedUi->urlHintsModifierAlt->isChecked()) { 2112 modifiers |= Qt::AltModifier; 2113 } 2114 if (_advancedUi->urlHintsModifierMeta->isChecked()) { 2115 modifiers |= Qt::MetaModifier; 2116 } 2117 updateTempProfileProperty(Profile::UrlHintsModifiers, int(modifiers)); 2118 } 2119 2120 void EditProfileDialog::toggleReverseUrlHints(bool enable) 2121 { 2122 updateTempProfileProperty(Profile::ReverseUrlHints, enable); 2123 } 2124 2125 void EditProfileDialog::toggleBlinkingText(bool enable) 2126 { 2127 updateTempProfileProperty(Profile::BlinkingTextEnabled, enable); 2128 } 2129 2130 void EditProfileDialog::toggleFlowControl(bool enable) 2131 { 2132 updateTempProfileProperty(Profile::FlowControlEnabled, enable); 2133 } 2134 2135 void EditProfileDialog::peekPrimaryKeySequenceChanged() 2136 { 2137 updateTempProfileProperty(Profile::PeekPrimaryKeySequence, _advancedUi->peekPrimaryWidget->keySequence().toString()); 2138 } 2139 2140 void EditProfileDialog::toggleWordMode(bool mode) 2141 { 2142 updateTempProfileProperty(Profile::WordMode, mode); 2143 _appearanceUi->wordModeAttr->setEnabled(mode); 2144 _appearanceUi->wordModeAscii->setEnabled(mode); 2145 _appearanceUi->wordModeBrahmic->setEnabled(mode); 2146 } 2147 2148 void EditProfileDialog::toggleWordModeAttr(bool mode) 2149 { 2150 updateTempProfileProperty(Profile::WordModeAttr, mode); 2151 } 2152 2153 void EditProfileDialog::toggleWordModeAscii(bool mode) 2154 { 2155 updateTempProfileProperty(Profile::WordModeAscii, mode); 2156 } 2157 2158 void EditProfileDialog::toggleWordModeBrahmic(bool mode) 2159 { 2160 updateTempProfileProperty(Profile::WordModeBrahmic, mode); 2161 } 2162 2163 void EditProfileDialog::toggleIgnoreWcWidth(bool ignore) 2164 { 2165 updateTempProfileProperty(Profile::IgnoreWcWidth, ignore); 2166 } 2167 2168 #include "moc_EditProfileDialog.cpp"