diff --git a/src/EditProfileDialog.cpp b/src/EditProfileDialog.cpp index 68436fcc..9556897f 100644 --- a/src/EditProfileDialog.cpp +++ b/src/EditProfileDialog.cpp @@ -1,1770 +1,1764 @@ /* Copyright 2007-2008 by Robert Knight Copyright 2018 by Harald Sitter 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" // Standard #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE #include #include #include #include #include #include // Konsole #include "ColorSchemeManager.h" #include "ui_EditProfileDialog.h" #include "KeyBindingEditor.h" #include "KeyboardTranslator.h" #include "KeyboardTranslatorManager.h" #include "ProfileManager.h" #include "ShellCommand.h" #include "WindowSystemInfo.h" #include "Shortcut_p.h" using namespace Konsole; EditProfileDialog::EditProfileDialog(QWidget *aParent) : QDialog(aParent), _ui(nullptr), _tempProfile(nullptr), _profile(nullptr), _pageNeedsUpdate(QVector()), _previewedProperties(QHash()), _delayedPreviewProperties(QHash()), _delayedPreviewTimer(new QTimer(this)), _colorDialog(nullptr), mButtonBox(nullptr) { setWindowTitle(i18n("Edit Profile")); mButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel|QDialogButtonBox::Apply); auto mainWidget = new QWidget(this); auto mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(mainWidget); QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); connect(mButtonBox, &QDialogButtonBox::accepted, this, &Konsole::EditProfileDialog::accept); connect(mButtonBox, &QDialogButtonBox::rejected, this, &Konsole::EditProfileDialog::reject); // disable the apply button , since no modification has been made mButtonBox->button(QDialogButtonBox::Apply)->setEnabled(false); connect(mButtonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &Konsole::EditProfileDialog::apply); connect(_delayedPreviewTimer, &QTimer::timeout, this, &Konsole::EditProfileDialog::delayedPreviewActivate); _ui = new Ui::EditProfileDialog(); _ui->setupUi(mainWidget); mainLayout->addWidget(mButtonBox); // 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 _pageNeedsUpdate.resize(_ui->tabWidget->count()); connect(_ui->tabWidget, &QTabWidget::currentChanged, this, &Konsole::EditProfileDialog::preparePage); createTempProfile(); } EditProfileDialog::~EditProfileDialog() { delete _ui; } 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 iter(_tempProfile->setProperties()); while (iter.hasNext()) { iter.next(); _previewedProperties.remove(iter.key()); } createTempProfile(); mButtonBox->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 (mButtonBox->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("

Konsole does not have permission to save this profile to:
\"%1\"

" "

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.

", _profile->path())); return false; } } const QList existingProfiles = ProfileManager::instance()->allProfiles(); QStringList otherExistingProfileNames; foreach(auto 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("

Each profile must have a name before it can be saved " "into disk.

")); // revert the name in the dialog _ui->profileNameEdit->setText(_profile->name()); selectProfileName(); return false; } else if (!_tempProfile->name().isEmpty() && otherExistingProfileNames.contains(_tempProfile->name())) { KMessageBox::sorry(this, i18n("

A profile with this name already exists.

")); // revert the name in the dialog _ui->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(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 _pageNeedsUpdate.fill(true); preparePage(_ui->tabWidget->currentIndex()); 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(int page) { const Profile::Ptr profile = lookupProfile(); Q_ASSERT(_pageNeedsUpdate.count() > page); Q_ASSERT(profile); QWidget *pageWidget = _ui->tabWidget->widget(page); if (_pageNeedsUpdate[page]) { if (pageWidget == _ui->generalTab) { setupGeneralPage(profile); } else if (pageWidget == _ui->tabsTab) { setupTabsPage(profile); } else if (pageWidget == _ui->appearanceTab) { setupAppearancePage(profile); } else if (pageWidget == _ui->scrollingTab) { setupScrollingPage(profile); } else if (pageWidget == _ui->keyboardTab) { setupKeyboardPage(profile); } else if (pageWidget == _ui->mouseTab) { setupMousePage(profile); } else if (pageWidget == _ui->advancedTab) { setupAdvancedPage(profile); } else { Q_ASSERT(false); } _pageNeedsUpdate[page] = false; } } void EditProfileDialog::selectProfileName() { _ui->profileNameEdit->setFocus(); _ui->profileNameEdit->selectAll(); } void EditProfileDialog::setupGeneralPage(const Profile::Ptr profile) { // basic profile options { _ui->profileNameEdit->setPlaceholderText(i18nc("@label:textbox", "Enter descriptive label")); ProfileGroup::Ptr group = profile->asGroup(); if (!group || group->profiles().count() < 2) { _ui->profileNameEdit->setText(profile->name()); _ui->profileNameEdit->setClearButtonEnabled(true); } else { _ui->profileNameEdit->setText(groupProfileNames(group, -1)); _ui->profileNameEdit->setEnabled(false); _ui->profileNameLabel->setEnabled(false); } } ShellCommand command(profile->command(), profile->arguments()); _ui->commandEdit->setText(command.fullCommand()); // If a "completion" is requested, consider changing this to KLineEdit // and using KCompletion. _ui->initialDirEdit->setText(profile->defaultWorkingDirectory()); _ui->initialDirEdit->setClearButtonEnabled(true); _ui->dirSelectButton->setIcon(QIcon::fromTheme(QStringLiteral("folder-open"))); _ui->iconSelectButton->setIcon(QIcon::fromTheme(profile->icon())); _ui->startInSameDirButton->setChecked(profile->startInCurrentSessionDir()); // terminal options _ui->terminalColumnsEntry->setValue(profile->terminalColumns()); _ui->terminalRowsEntry->setValue(profile->terminalRows()); // window options _ui->showTerminalSizeHintButton->setChecked(profile->showTerminalSizeHint()); // signals and slots connect(_ui->dirSelectButton, &QToolButton::clicked, this, &Konsole::EditProfileDialog::selectInitialDir); connect(_ui->iconSelectButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::selectIcon); connect(_ui->startInSameDirButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::startInSameDir); connect(_ui->profileNameEdit, &QLineEdit::textChanged, this, &Konsole::EditProfileDialog::profileNameChanged); connect(_ui->initialDirEdit, &QLineEdit::textChanged, this, &Konsole::EditProfileDialog::initialDirChanged); connect(_ui->commandEdit, &QLineEdit::textChanged, this, &Konsole::EditProfileDialog::commandChanged); connect(_ui->environmentEditButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::showEnvironmentEditor); connect(_ui->terminalColumnsEntry, static_cast(&QSpinBox::valueChanged), this, &Konsole::EditProfileDialog::terminalColumnsEntryChanged); connect(_ui->terminalRowsEntry, static_cast(&QSpinBox::valueChanged), this, &Konsole::EditProfileDialog::terminalRowsEntryChanged); connect(_ui->showTerminalSizeHintButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::showTerminalSizeHint); } 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 _ui->renameTabWidget->setTabTitleText(profile->localTabTitleFormat()); _ui->renameTabWidget->setRemoteTabTitleText(profile->remoteTabTitleFormat()); connect(_ui->renameTabWidget, &Konsole::RenameTabWidget::tabTitleFormatChanged, this, &Konsole::EditProfileDialog::tabTitleFormatChanged); connect(_ui->renameTabWidget, &Konsole::RenameTabWidget::remoteTabTitleFormatChanged, this, &Konsole::EditProfileDialog::remoteTabTitleFormatChanged); // tab monitoring const int silenceSeconds = profile->silenceSeconds(); _ui->silenceSecondsSpinner->setValue(silenceSeconds); _ui->silenceSecondsSpinner->setSuffix(ki18ncp("Unit of time", " second", " seconds")); connect(_ui->silenceSecondsSpinner, static_cast(&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::tabTitleFormatChanged(const QString &format) { updateTempProfileProperty(Profile::LocalTabTitleFormat, format); } void EditProfileDialog::remoteTabTitleFormatChanged(const QString &format) { updateTempProfileProperty(Profile::RemoteTabTitleFormat, format); } 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()) { _ui->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(_ui->initialDirEdit-> text())); if (!url.isEmpty()) { _ui->initialDirEdit->setText(url.path()); } } void EditProfileDialog::setupAppearancePage(const Profile::Ptr profile) { auto delegate = new ColorSchemeViewDelegate(this); _ui->colorSchemeList->setItemDelegate(delegate); _ui->transparencyWarningWidget->setVisible(false); _ui->transparencyWarningWidget->setWordWrap(true); _ui->transparencyWarningWidget->setCloseButtonVisible(false); _ui->transparencyWarningWidget->setMessageType(KMessageWidget::Warning); _ui->colorSchemeMessageWidget->setVisible(false); _ui->colorSchemeMessageWidget->setWordWrap(true); _ui->colorSchemeMessageWidget->setCloseButtonVisible(false); _ui->colorSchemeMessageWidget->setMessageType(KMessageWidget::Warning); _ui->editColorSchemeButton->setEnabled(false); _ui->removeColorSchemeButton->setEnabled(false); _ui->resetColorSchemeButton->setEnabled(false); _ui->downloadColorSchemeButton->setConfigFile(QStringLiteral("konsole.knsrc")); // setup color list // select the colorScheme used in the current profile updateColorSchemeList(currentColorSchemeName()); _ui->colorSchemeList->setMouseTracking(true); _ui->colorSchemeList->installEventFilter(this); _ui->colorSchemeList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); connect(_ui->colorSchemeList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Konsole::EditProfileDialog::colorSchemeSelected); connect(_ui->colorSchemeList, &QListView::entered, this, &Konsole::EditProfileDialog::previewColorScheme); updateColorSchemeButtons(); connect(_ui->editColorSchemeButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::editColorScheme); connect(_ui->removeColorSchemeButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::removeColorScheme); connect(_ui->newColorSchemeButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::newColorScheme); connect(_ui->downloadColorSchemeButton, &KNS3::Button::dialogFinished, this, &Konsole::EditProfileDialog::gotNewColorSchemes); connect(_ui->resetColorSchemeButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::resetColorScheme); // setup font preview const bool antialias = profile->antiAliasFonts(); QFont profileFont = profile->font(); profileFont.setStyleStrategy(antialias ? QFont::PreferAntialias : QFont::NoAntialias); _ui->fontPreviewLabel->installEventFilter(this); _ui->fontPreviewLabel->setFont(profileFont); setFontInputValue(profileFont); // Always set to unchecked _ui->showAllFontsButton->setChecked(false); connect(_ui->showAllFontsButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::showAllFontsButtonWarning); connect(_ui->fontSizeInput, static_cast(&QDoubleSpinBox::valueChanged), this, &Konsole::EditProfileDialog::setFontSize); connect(_ui->selectFontButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::showFontDialog); // setup font smoothing _ui->antialiasTextButton->setChecked(antialias); connect(_ui->antialiasTextButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::setAntialiasText); _ui->boldIntenseButton->setChecked(profile->boldIntense()); connect(_ui->boldIntenseButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::setBoldIntense); _ui->useFontLineCharactersButton->setChecked(profile->useFontLineCharacters()); connect(_ui->useFontLineCharactersButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::useFontLineCharacters); _ui->enableMouseWheelZoomButton->setChecked(profile->mouseWheelZoomEnabled()); connect(_ui->enableMouseWheelZoomButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::toggleMouseWheelZoom); } void EditProfileDialog::showAllFontsButtonWarning(bool enable) { if (enable) { KMessageBox::information(this, QStringLiteral( "By its very nature, a terminal program requires font characters that are equal width (monospace). Any non monospaced font may cause display issues. This should not be necessary except in rare cases."), QStringLiteral("Warning")); } } void EditProfileDialog::setAntialiasText(bool enable) { QFont profileFont = _ui->fontPreviewLabel->font(); profileFont.setStyleStrategy(enable ? QFont::PreferAntialias : QFont::NoAntialias); // update preview to reflect text smoothing state fontSelected(profileFont); updateTempProfileProperty(Profile::AntiAliasFonts, enable); } 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::toggleMouseWheelZoom(bool enable) { updateTempProfileProperty(Profile::MouseWheelZoomEnabled, enable); } void EditProfileDialog::toggleAlternateScrolling(bool enable) { updateTempProfileProperty(Profile::AlternateScrolling, enable); } void EditProfileDialog::updateColorSchemeList(const QString &selectedColorSchemeName) { if (_ui->colorSchemeList->model() == nullptr) { _ui->colorSchemeList->setModel(new QStandardItemModel(this)); } const ColorScheme *selectedColorScheme = ColorSchemeManager::instance()->findColorScheme(selectedColorSchemeName); QStandardItemModel *model = qobject_cast(_ui->colorSchemeList->model()); Q_ASSERT(model); model->clear(); QStandardItem *selectedItem = nullptr; QList schemeList = ColorSchemeManager::instance()->allColorSchemes(); foreach (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) { _ui->colorSchemeList->updateGeometry(); _ui->colorSchemeList->selectionModel()->setCurrentIndex(selectedItem->index(), QItemSelectionModel::Select); // update transparency warning label updateTransparencyWarning(); } } -void EditProfileDialog::updateKeyBindingsList(bool selectCurrentTranslator) +void EditProfileDialog::updateKeyBindingsList(const QString &selectKeyBindingsName) { if (_ui->keyBindingList->model() == nullptr) { _ui->keyBindingList->setModel(new QStandardItemModel(this)); } - const QString &name = lookupProfile()->keyBindings(); - - KeyboardTranslatorManager *keyManager = KeyboardTranslatorManager::instance(); - const KeyboardTranslator *currentTranslator = keyManager->findTranslator(name); - QStandardItemModel *model = qobject_cast(_ui->keyBindingList->model()); Q_ASSERT(model); model->clear(); QStandardItem *selectedItem = nullptr; - QStringList translatorNames = keyManager->allTranslators(); - foreach (const QString &translatorName, translatorNames) { - const KeyboardTranslator *translator = keyManager->findTranslator(translatorName); + 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 (translator == currentTranslator) { + if (selectKeyBindingsName == translatorName) { selectedItem = item; } model->appendRow(item); } model->sort(0); - if (selectCurrentTranslator && (selectedItem != nullptr)) { + if (selectedItem != nullptr) { _ui->keyBindingList->selectionModel()->setCurrentIndex(selectedItem->index(), QItemSelectionModel::Select); } } bool EditProfileDialog::eventFilter(QObject *watched, QEvent *event) { if (watched == _ui->colorSchemeList && event->type() == QEvent::Leave) { if (_tempProfile->isPropertySet(Profile::ColorScheme)) { preview(Profile::ColorScheme, _tempProfile->colorScheme()); } else { unpreview(Profile::ColorScheme); } } if (watched == _ui->fontPreviewLabel && event->type() == QEvent::FontChange) { const QFont &labelFont = _ui->fontPreviewLabel->font(); _ui->fontPreviewLabel->setText(i18n("%1", labelFont.family())); } return QDialog::eventFilter(watched, event); } void EditProfileDialog::unpreviewAll() { _delayedPreviewTimer->stop(); _delayedPreviewProperties.clear(); QHash map; QHashIterator iter(_previewedProperties); while (iter.hasNext()) { iter.next(); map.insert(static_cast(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 map; map.insert(static_cast(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(sender())); QMutableHashIterator iter(_delayedPreviewProperties); if (iter.hasNext()) { iter.next(); preview(iter.key(), iter.value()); } } void EditProfileDialog::preview(int property, const QVariant &value) { QHash map; map.insert(static_cast(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(static_cast(property)).isNull()) { return; } if (!_previewedProperties.contains(property)) { _previewedProperties.insert(property, original->property(static_cast(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()->name(); delayedPreview(Profile::ColorScheme, name); } void EditProfileDialog::removeColorScheme() { QModelIndexList selected = _ui->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, [=](const KNSCore::EntryInternal::List &entries) { const QString &name = selected.first().data(Qt::UserRole + 1).value()->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) { _ui->colorSchemeList->model()->removeRow(selected.first().row()); } manager->deleteLater(); }); manager->checkForInstalled(); return; } void EditProfileDialog::gotNewColorSchemes(const KNS3::Entry::List &changedEntries) { int failures = 0; for (auto entry : 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()) { _ui->colorSchemeMessageWidget->setText( xi18nc("@info", "Scheme %1 failed to load.", entry.name())); _ui->colorSchemeMessageWidget->animatedShow(); QTimer::singleShot(8000, _ui->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 = _ui->colorSchemeList->selectionModel()->selectedIndexes(); if (!selected.isEmpty()) { const QString &name = selected.first().data(Qt::UserRole + 1).value()->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 = _ui->colorSchemeList->selectionModel()->selectedIndexes(); QAbstractItemModel *model = _ui->colorSchemeList->model(); const ColorScheme *colors = nullptr; if (!selected.isEmpty()) { colors = model->data(selected.first(), Qt::UserRole + 1).value(); } 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 = _ui->colorSchemeList->selectionModel()->selectedIndexes(); if (!selected.isEmpty()) { QAbstractItemModel *model = _ui->colorSchemeList->model(); const ColorScheme *colors = model->data(selected.first(), Qt::UserRole + 1).value(); if (colors != nullptr) { updateTempProfileProperty(Profile::ColorScheme, colors->name()); previewColorScheme(selected.first()); updateTransparencyWarning(); } } updateColorSchemeButtons(); } void EditProfileDialog::updateColorSchemeButtons() { enableIfNonEmptySelection(_ui->editColorSchemeButton, _ui->colorSchemeList->selectionModel()); QModelIndexList selected = _ui->colorSchemeList->selectionModel()->selectedIndexes(); if (!selected.isEmpty()) { const QString &name = selected.first().data(Qt::UserRole + 1).value()->name(); bool isResettable = ColorSchemeManager::instance()->canResetColorScheme(name); _ui->resetColorSchemeButton->setEnabled(isResettable); bool isDeletable = ColorSchemeManager::instance()->isColorSchemeDeletable(name); // if a colorScheme can be restored then it can't be deleted _ui->removeColorSchemeButton->setEnabled(isDeletable && !isResettable); } else { _ui->removeColorSchemeButton->setEnabled(false); _ui->resetColorSchemeButton->setEnabled(false); } } void EditProfileDialog::updateKeyBindingsButtons() { - enableIfNonEmptySelection(_ui->editKeyBindingsButton, _ui->keyBindingList->selectionModel()); - enableIfNonEmptySelection(_ui->removeKeyBindingsButton, _ui->keyBindingList->selectionModel()); + QModelIndexList selected = _ui->keyBindingList->selectionModel()->selectedIndexes(); + + if (!selected.isEmpty()) { + _ui->editKeyBindingsButton->setEnabled(true); + + const QString &name = selected.first().data(Qt::UserRole + 1).value()->name(); + + bool isResettable = _keyManager->isTranslatorResettable(name); + _ui->resetKeyBindingsButton->setEnabled(isResettable); + + bool isDeletable = _keyManager->isTranslatorDeletable(name); + + // if a key bindings scheme can be reset then it can't be deleted + _ui->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 foreach (const QModelIndex &index, _ui->colorSchemeList->selectionModel()->selectedIndexes()) { bool needTransparency = index.data(Qt::UserRole + 1).value()->opacity() < 1.0; if (!needTransparency) { _ui->transparencyWarningWidget->setHidden(true); } else if (!KWindowSystem::compositingActive()) { _ui->transparencyWarningWidget->setText(i18n( "This color scheme uses a transparent background" " which does not appear to be supported on your" " desktop")); _ui->transparencyWarningWidget->setHidden(false); } else if (!WindowSystemInfo::HAVE_TRANSPARENCY) { _ui->transparencyWarningWidget->setText(i18n( "Konsole was started before desktop effects were enabled." " You need to restart Konsole to see transparent background.")); _ui->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 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(property))) { if (value != _previewedProperties.value(static_cast(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(property))) { userModified = true; break; } } mButtonBox->button(QDialogButtonBox::Apply)->setEnabled(userModified); } void EditProfileDialog::setupKeyboardPage(const Profile::Ptr /* profile */) { // setup translator list - updateKeyBindingsList(true); + updateKeyBindingsList(lookupProfile()->keyBindings()); connect(_ui->keyBindingList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Konsole::EditProfileDialog::keyBindingSelected); connect(_ui->newKeyBindingsButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::newKeyBinding); + _ui->editKeyBindingsButton->setEnabled(false); + _ui->removeKeyBindingsButton->setEnabled(false); + _ui->resetKeyBindingsButton->setEnabled(false); + updateKeyBindingsButtons(); connect(_ui->editKeyBindingsButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::editKeyBinding); connect(_ui->removeKeyBindingsButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::removeKeyBinding); + connect(_ui->resetKeyBindingsButton, &QPushButton::clicked, this, + &Konsole::EditProfileDialog::resetKeyBindings); } void EditProfileDialog::keyBindingSelected() { QModelIndexList selected = _ui->keyBindingList->selectionModel()->selectedIndexes(); if (!selected.isEmpty()) { QAbstractItemModel *model = _ui->keyBindingList->model(); const KeyboardTranslator *translator = model->data(selected.first(), Qt::UserRole + 1) .value(); if (translator != nullptr) { updateTempProfileProperty(Profile::KeyBindings, translator->name()); } } updateKeyBindingsButtons(); } void EditProfileDialog::removeKeyBinding() { QModelIndexList selected = _ui->keyBindingList->selectionModel()->selectedIndexes(); if (!selected.isEmpty()) { const QString &name = selected.first().data(Qt::UserRole + 1).value()->name(); if (KeyboardTranslatorManager::instance()->deleteTranslator(name)) { _ui->keyBindingList->model()->removeRow(selected.first().row()); } } } void EditProfileDialog::showKeyBindingEditor(bool isNewTranslator) { QModelIndexList selected = _ui->keyBindingList->selectionModel()->selectedIndexes(); QAbstractItemModel *model = _ui->keyBindingList->model(); const KeyboardTranslator *translator = nullptr; if (!selected.isEmpty()) { translator = model->data(selected.first(), Qt::UserRole + 1).value(); } else { - translator = KeyboardTranslatorManager::instance()->defaultTranslator(); + translator = _keyManager->defaultTranslator(); } Q_ASSERT(translator); - QPointer dialog = new QDialog(this); - auto buttonBox = new QDialogButtonBox(dialog); - buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - connect(buttonBox, &QDialogButtonBox::accepted, dialog.data(), &QDialog::accept); - connect(buttonBox, &QDialogButtonBox::rejected, dialog.data(), &QDialog::reject); - - if (isNewTranslator) { - dialog->setWindowTitle(i18n("New Key Binding List")); - } else { - dialog->setWindowTitle(i18n("Edit Key Binding List")); - } - - auto editor = new KeyBindingEditor; + auto editor = new KeyBindingEditor(this); if (translator != nullptr) { - editor->setup(translator); - } - - if (isNewTranslator) { - editor->setDescription(i18n("New Key Binding List")); + editor->setup(translator, lookupProfile()->keyBindings(), isNewTranslator); } - auto layout = new QVBoxLayout; - layout->addWidget(editor); - layout->addWidget(buttonBox); - dialog->setLayout(layout); - // see also the size set in the KeyBindingEditor constructor - dialog->setMinimumSize(480, 430); - dialog->resize(500, 500); - - if (dialog->exec() == QDialog::Accepted) { - auto newTranslator = new KeyboardTranslator(*editor->translator()); - - if (isNewTranslator) { - newTranslator->setName(newTranslator->description()); - } - - KeyboardTranslatorManager::instance()->addTranslator(newTranslator); + connect(editor, &Konsole::KeyBindingEditor::updateKeyBindingsListRequest, + this, &Konsole::EditProfileDialog::updateKeyBindingsList); - updateKeyBindingsList(); + connect(editor, &Konsole::KeyBindingEditor::updateTempProfileKeyBindingsRequest, + this, &Konsole::EditProfileDialog::updateTempProfileProperty); - const QString ¤tTranslator = lookupProfile() - ->property(Profile::KeyBindings); - - if (newTranslator->name() == currentTranslator) { - updateTempProfileProperty(Profile::KeyBindings, newTranslator->name()); - } - } - delete dialog; + editor->exec(); } void EditProfileDialog::newKeyBinding() { showKeyBindingEditor(true); } void EditProfileDialog::editKeyBinding() { showKeyBindingEditor(false); } +void EditProfileDialog::resetKeyBindings() +{ + QModelIndexList selected = _ui->keyBindingList->selectionModel()->selectedIndexes(); + + if (!selected.isEmpty()) { + const QString &name = selected.first().data(Qt::UserRole + 1).value()->name(); + + _keyManager->deleteTranslator(name); + // find and load the translator + _keyManager->findTranslator(name); + + updateKeyBindingsList(name); + } +} + void EditProfileDialog::setupCheckBoxes(BooleanOption *options, const Profile::Ptr profile) { while (options->button != nullptr) { options->button->setChecked(profile->property(options->property)); connect(options->button, SIGNAL(toggled(bool)), this, options->slot); ++options; } } void EditProfileDialog::setupRadio(RadioOption *possibilities, int actual) { while (possibilities->button != nullptr) { if (possibilities->value == actual) { possibilities->button->setChecked(true); } else { possibilities->button->setChecked(false); } connect(possibilities->button, SIGNAL(clicked()), this, possibilities->slot); ++possibilities; } } void EditProfileDialog::setupScrollingPage(const Profile::Ptr profile) { // setup scrollbar radio int scrollBarPosition = profile->property(Profile::ScrollBarPosition); RadioOption positions[] = { {_ui->scrollBarHiddenButton, Enum::ScrollBarHidden, SLOT(hideScrollBar())}, {_ui->scrollBarLeftButton, Enum::ScrollBarLeft, SLOT(showScrollBarLeft())}, {_ui->scrollBarRightButton, Enum::ScrollBarRight, SLOT(showScrollBarRight())}, {nullptr, 0, nullptr}}; setupRadio(positions, scrollBarPosition); // setup scrollback type radio int scrollBackType = profile->property(Profile::HistoryMode); _ui->historySizeWidget->setMode(Enum::HistoryModeEnum(scrollBackType)); connect(_ui->historySizeWidget, &Konsole::HistorySizeWidget::historyModeChanged, this, &Konsole::EditProfileDialog::historyModeChanged); // setup scrollback line count spinner const int historySize = profile->historySize(); _ui->historySizeWidget->setLineCount(historySize); // setup scrollpageamount type radio int scrollFullPage = profile->property(Profile::ScrollFullPage); RadioOption pageamounts[] = { {_ui->scrollHalfPage, Enum::ScrollPageHalf, SLOT(scrollHalfPage())}, {_ui->scrollFullPage, Enum::ScrollPageFull, SLOT(scrollFullPage())}, {nullptr, 0, nullptr} }; setupRadio(pageamounts, scrollFullPage); // signals and slots connect(_ui->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::hideScrollBar() { updateTempProfileProperty(Profile::ScrollBarPosition, Enum::ScrollBarHidden); } void EditProfileDialog::showScrollBarLeft() { updateTempProfileProperty(Profile::ScrollBarPosition, Enum::ScrollBarLeft); } void EditProfileDialog::showScrollBarRight() { updateTempProfileProperty(Profile::ScrollBarPosition, Enum::ScrollBarRight); } void EditProfileDialog::scrollFullPage() { updateTempProfileProperty(Profile::ScrollFullPage, Enum::ScrollPageFull); } void EditProfileDialog::scrollHalfPage() { updateTempProfileProperty(Profile::ScrollFullPage, Enum::ScrollPageHalf); } void EditProfileDialog::setupMousePage(const Profile::Ptr profile) { BooleanOption options[] = { { _ui->underlineLinksButton, Profile::UnderlineLinksEnabled, SLOT(toggleUnderlineLinks(bool)) }, { _ui->underlineFilesButton, Profile::UnderlineFilesEnabled, SLOT(toggleUnderlineFiles(bool)) }, { _ui->ctrlRequiredForDragButton, Profile::CtrlRequiredForDrag, SLOT(toggleCtrlRequiredForDrag(bool)) }, { _ui->copyTextAsHTMLButton, Profile::CopyTextAsHTML, SLOT(toggleCopyTextAsHTML(bool)) }, { _ui->copyTextToClipboardButton, Profile::AutoCopySelectedText, SLOT(toggleCopyTextToClipboard(bool)) }, { _ui->trimLeadingSpacesButton, Profile::TrimLeadingSpacesInSelectedText, SLOT(toggleTrimLeadingSpacesInSelectedText(bool)) }, { _ui->trimTrailingSpacesButton, Profile::TrimTrailingSpacesInSelectedText, SLOT(toggleTrimTrailingSpacesInSelectedText(bool)) }, { _ui->openLinksByDirectClickButton, Profile::OpenLinksByDirectClickEnabled, SLOT(toggleOpenLinksByDirectClick(bool)) }, { _ui->dropUrlsAsText, Profile::DropUrlsAsText, SLOT(toggleDropUrlsAsText(bool)) }, { _ui->enableAlternateScrollingButton, Profile::AlternateScrolling, SLOT(toggleAlternateScrolling(bool)) }, { nullptr, Profile::Property(0), nullptr } }; setupCheckBoxes(options, profile); // setup middle click paste mode const int middleClickPasteMode = profile->property(Profile::MiddleClickPasteMode); RadioOption pasteModes[] = { {_ui->pasteFromX11SelectionButton, Enum::PasteFromX11Selection, SLOT(pasteFromX11Selection())}, {_ui->pasteFromClipboardButton, Enum::PasteFromClipboard, SLOT(pasteFromClipboard())}, {nullptr, 0, nullptr} }; setupRadio(pasteModes, middleClickPasteMode); // interaction options _ui->wordCharacterEdit->setText(profile->wordCharacters()); connect(_ui->wordCharacterEdit, &QLineEdit::textChanged, this, &Konsole::EditProfileDialog::wordCharactersChanged); int tripleClickMode = profile->property(Profile::TripleClickMode); _ui->tripleClickModeCombo->setCurrentIndex(tripleClickMode); connect(_ui->tripleClickModeCombo, static_cast(&KComboBox::activated), this, &Konsole::EditProfileDialog::TripleClickModeChanged); _ui->openLinksByDirectClickButton->setEnabled(_ui->underlineLinksButton->isChecked() || _ui->underlineFilesButton->isChecked()); _ui->enableMouseWheelZoomButton->setChecked(profile->mouseWheelZoomEnabled()); connect(_ui->enableMouseWheelZoomButton, &QCheckBox::toggled, this, &Konsole::EditProfileDialog::toggleMouseWheelZoom); } void EditProfileDialog::setupAdvancedPage(const Profile::Ptr profile) { BooleanOption options[] = { { _ui->enableBlinkingTextButton, Profile::BlinkingTextEnabled, SLOT(toggleBlinkingText(bool)) }, { _ui->enableFlowControlButton, Profile::FlowControlEnabled, SLOT(toggleFlowControl(bool)) }, { _ui->enableBlinkingCursorButton, Profile::BlinkingCursorEnabled, SLOT(toggleBlinkingCursor(bool)) }, { _ui->enableBidiRenderingButton, Profile::BidiRenderingEnabled, SLOT(togglebidiRendering(bool)) }, { nullptr, Profile::Property(0), nullptr } }; setupCheckBoxes(options, profile); // Setup the URL hints modifier checkboxes { int modifiers = profile->property(Profile::UrlHintsModifiers); _ui->urlHintsModifierShift->setChecked((modifiers &Qt::ShiftModifier) != 0u); _ui->urlHintsModifierCtrl->setChecked((modifiers &Qt::ControlModifier) != 0u); _ui->urlHintsModifierAlt->setChecked((modifiers &Qt::AltModifier) != 0u); _ui->urlHintsModifierMeta->setChecked((modifiers &Qt::MetaModifier) != 0u); connect(_ui->urlHintsModifierShift, &QCheckBox::toggled, this, &EditProfileDialog::updateUrlHintsModifier); connect(_ui->urlHintsModifierCtrl, &QCheckBox::toggled, this, &EditProfileDialog::updateUrlHintsModifier); connect(_ui->urlHintsModifierAlt, &QCheckBox::toggled, this, &EditProfileDialog::updateUrlHintsModifier); connect(_ui->urlHintsModifierMeta, &QCheckBox::toggled, this, &EditProfileDialog::updateUrlHintsModifier); } const int lineSpacing = profile->lineSpacing(); _ui->lineSpacingSpinner->setValue(lineSpacing); connect(_ui->lineSpacingSpinner, static_cast(&QSpinBox::valueChanged), this, &Konsole::EditProfileDialog::lineSpacingChanged); // cursor options if (profile->useCustomCursorColor()) { _ui->customCursorColorButton->setChecked(true); } else { _ui->autoCursorColorButton->setChecked(true); } _ui->customColorSelectButton->setColor(profile->customCursorColor()); connect(_ui->customCursorColorButton, &QRadioButton::clicked, this, &Konsole::EditProfileDialog::customCursorColor); connect(_ui->autoCursorColorButton, &QRadioButton::clicked, this, &Konsole::EditProfileDialog::autoCursorColor); connect(_ui->customColorSelectButton, &KColorButton::changed, this, &Konsole::EditProfileDialog::customCursorColorChanged); int shape = profile->property(Profile::CursorShape); _ui->cursorShapeCombo->setCurrentIndex(shape); connect(_ui->cursorShapeCombo, static_cast(&KComboBox::activated), this, &Konsole::EditProfileDialog::setCursorShape); // encoding options auto codecAction = new KCodecAction(this); _ui->selectEncodingButton->setMenu(codecAction->menu()); connect(codecAction, static_cast(&KCodecAction::triggered), this, &Konsole::EditProfileDialog::setDefaultCodec); _ui->characterEncodingLabel->setText(profile->defaultEncoding()); } void EditProfileDialog::setDefaultCodec(QTextCodec *codec) { QString name = QString::fromLocal8Bit(codec->name()); updateTempProfileProperty(Profile::DefaultEncoding, name); _ui->characterEncodingLabel->setText(name); } void EditProfileDialog::customCursorColorChanged(const QColor &color) { updateTempProfileProperty(Profile::CustomCursorColor, color); // ensure that custom cursor colors are enabled _ui->customCursorColorButton->click(); } void EditProfileDialog::wordCharactersChanged(const QString &text) { updateTempProfileProperty(Profile::WordCharacters, text); } void EditProfileDialog::autoCursorColor() { updateTempProfileProperty(Profile::UseCustomCursorColor, false); } void EditProfileDialog::customCursorColor() { updateTempProfileProperty(Profile::UseCustomCursorColor, true); } void EditProfileDialog::setCursorShape(int index) { updateTempProfileProperty(Profile::CursorShape, index); } void EditProfileDialog::togglebidiRendering(bool enable) { updateTempProfileProperty(Profile::BidiRenderingEnabled, enable); } void EditProfileDialog::lineSpacingChanged(int spacing) { updateTempProfileProperty(Profile::LineSpacing, spacing); } void EditProfileDialog::toggleBlinkingCursor(bool enable) { updateTempProfileProperty(Profile::BlinkingCursorEnabled, enable); } void EditProfileDialog::toggleUnderlineLinks(bool enable) { updateTempProfileProperty(Profile::UnderlineLinksEnabled, enable); bool enableClick = _ui->underlineFilesButton->isChecked() || enable; _ui->openLinksByDirectClickButton->setEnabled(enableClick); } void EditProfileDialog::toggleUnderlineFiles(bool enable) { updateTempProfileProperty(Profile::UnderlineFilesEnabled, enable); bool enableClick = _ui->underlineLinksButton->isChecked() || enable; _ui->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 (_ui->urlHintsModifierShift->isChecked()) { modifiers |= Qt::ShiftModifier; } if (_ui->urlHintsModifierCtrl->isChecked()) { modifiers |= Qt::ControlModifier; } if (_ui->urlHintsModifierAlt->isChecked()) { modifiers |= Qt::AltModifier; } if (_ui->urlHintsModifierMeta->isChecked()) { modifiers |= Qt::MetaModifier; } updateTempProfileProperty(Profile::UrlHintsModifiers, int(modifiers)); } void EditProfileDialog::toggleBlinkingText(bool enable) { updateTempProfileProperty(Profile::BlinkingTextEnabled, enable); } void EditProfileDialog::toggleFlowControl(bool enable) { updateTempProfileProperty(Profile::FlowControlEnabled, enable); } void EditProfileDialog::fontSelected(const QFont &aFont) { QFont previewFont = aFont; setFontInputValue(aFont); _ui->fontPreviewLabel->setFont(previewFont); preview(Profile::Font, aFont); updateTempProfileProperty(Profile::Font, aFont); } void EditProfileDialog::showFontDialog() { QFont currentFont = _ui->fontPreviewLabel->font(); bool showAllFonts = _ui->showAllFontsButton->isChecked(); bool result; if (showAllFonts) { currentFont = QFontDialog::getFont(&result, currentFont, this, i18n("Select Any Font")); } else { currentFont = QFontDialog::getFont(&result, currentFont, this, i18n("Select Fixed Width Font"), QFontDialog::MonospacedFonts); } if (!result) { return; } fontSelected(currentFont); } void EditProfileDialog::setFontSize(double pointSize) { QFont newFont = _ui->fontPreviewLabel->font(); newFont.setPointSizeF(pointSize); _ui->fontPreviewLabel->setFont(newFont); preview(Profile::Font, newFont); updateTempProfileProperty(Profile::Font, newFont); } void EditProfileDialog::setFontInputValue(const QFont &aFont) { _ui->fontSizeInput->setValue(aFont.pointSizeF()); } ColorSchemeViewDelegate::ColorSchemeViewDelegate(QObject *aParent) : QAbstractItemDelegate(aParent) { } void ColorSchemeViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { const ColorScheme *scheme = index.data(Qt::UserRole + 1).value(); QFont profileFont = index.data(Qt::UserRole + 2).value(); Q_ASSERT(scheme); if (scheme == nullptr) { return; } painter->setRenderHint(QPainter::Antialiasing); // Draw background QStyle *style = option.widget != nullptr ? option.widget->style() : QApplication::style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, option.widget); // Draw name QPalette::ColorRole textColor = ((option.state & QStyle::State_Selected) != 0) ? QPalette::HighlightedText : QPalette::Text; painter->setPen(option.palette.color(textColor)); painter->setFont(option.font); // Determine width of sample text using profile's font const QString sampleText = i18n("AaZz09..."); QFontMetrics profileFontMetrics(profileFont); const int sampleTextWidth = profileFontMetrics.width(sampleText); painter->drawText(option.rect.adjusted(sampleTextWidth + 15, 0, 0, 0), Qt::AlignLeft | Qt::AlignVCenter, index.data(Qt::DisplayRole).toString()); // Draw the preview const int x = option.rect.left(); const int y = option.rect.top(); QRect previewRect(x + 4, y + 4, sampleTextWidth + 8, option.rect.height() - 8); bool transparencyAvailable = KWindowSystem::compositingActive(); if (transparencyAvailable) { painter->save(); QColor color = scheme->backgroundColor(); color.setAlphaF(scheme->opacity()); painter->setPen(Qt::NoPen); painter->setCompositionMode(QPainter::CompositionMode_Source); painter->setBrush(color); painter->drawRect(previewRect); painter->restore(); } else { painter->setPen(Qt::NoPen); painter->setBrush(scheme->backgroundColor()); painter->drawRect(previewRect); } // draw color scheme name using scheme's foreground color QPen pen(scheme->foregroundColor()); painter->setPen(pen); // TODO: respect antialias setting painter->setFont(profileFont); painter->drawText(previewRect, Qt::AlignCenter, sampleText); } QSize ColorSchemeViewDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex & /*index*/) const { const int width = 200; const int margin = 5; const int colorWidth = width / TABLE_COLORS; const int heightForWidth = (colorWidth * 2) + option.fontMetrics.height() + margin; // temporary return QSize(width, heightForWidth); } diff --git a/src/EditProfileDialog.h b/src/EditProfileDialog.h index 4f859ac9..8354bfd8 100644 --- a/src/EditProfileDialog.h +++ b/src/EditProfileDialog.h @@ -1,310 +1,318 @@ /* Copyright 2007-2008 by Robert Knight Copyright 2018 by Harald Sitter 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. */ #ifndef EDITPROFILEDIALOG_H #define EDITPROFILEDIALOG_H // Qt #include #include #include #include // KDE #include // Konsole #include "Profile.h" #include "Enumeration.h" #include "ColorScheme.h" #include "ColorSchemeEditor.h" +#include "KeyboardTranslatorManager.h" class QAbstractButton; class QItemSelectionModel; class QTextCodec; class QDialogButtonBox; namespace Ui { class EditProfileDialog; } namespace Konsole { /** * A dialog which allows the user to edit a profile. * After the dialog is created, it can be initialized with the settings * for a profile using setProfile(). When the user makes changes to the * dialog and accepts the changes, the dialog will update the * profile in the SessionManager by calling the SessionManager's * changeProfile() method. * * Some changes made in the dialog are preview-only changes which cause * the SessionManager's changeProfile() method to be called with * the persistent argument set to false. These changes are then * un-done when the dialog is closed. */ class KONSOLEPRIVATE_EXPORT EditProfileDialog : public QDialog { Q_OBJECT public: /** Constructs a new dialog with the specified parent. */ explicit EditProfileDialog(QWidget *parent = nullptr); ~EditProfileDialog() Q_DECL_OVERRIDE; /** * Initializes the dialog with the settings for the specified session * type. * * When the dialog closes, the profile will be updated in the SessionManager * with the altered settings. * * @param profile The profile to be edited */ void setProfile(Profile::Ptr profile); /** * Selects the text in the profile name edit area. * When the dialog is being used to create a new profile, * this can be used to draw the user's attention to the profile name * and make it easy for them to change it. */ void selectProfileName(); const Profile::Ptr lookupProfile() const; public Q_SLOTS: // reimplemented void accept() Q_DECL_OVERRIDE; // reimplemented void reject() Q_DECL_OVERRIDE; void apply(); protected: bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE; private Q_SLOTS: // sets up the specified tab page if necessary void preparePage(int); // saves changes to profile void save(); // general page void selectInitialDir(); void selectIcon(); void profileNameChanged(const QString &name); void initialDirChanged(const QString &dir); void startInSameDir(bool); void commandChanged(const QString &command); void tabTitleFormatChanged(const QString &format); void remoteTabTitleFormatChanged(const QString &format); void terminalColumnsEntryChanged(int); void terminalRowsEntryChanged(int); void showTerminalSizeHint(bool); void showEnvironmentEditor(); void silenceSecondsChanged(int); // appearance page void setFontSize(double pointSize); void setFontInputValue(const QFont &); void showAllFontsButtonWarning(bool enable); void setAntialiasText(bool enable); void setBoldIntense(bool enable); void useFontLineCharacters(bool enable); void showFontDialog(); void newColorScheme(); void editColorScheme(); void saveColorScheme(const ColorScheme &scheme, bool isNewScheme); void removeColorScheme(); void gotNewColorSchemes(const KNS3::Entry::List &changedEntries); /** * Deletes the selected colorscheme from the user's home dir location * so that the original one from the system-wide location can be used * instead */ void resetColorScheme(); void colorSchemeSelected(); void previewColorScheme(const QModelIndex &index); void fontSelected(const QFont &); void toggleMouseWheelZoom(bool enable); // scrolling page void historyModeChanged(Enum::HistoryModeEnum mode); void historySizeChanged(int); void hideScrollBar(); void showScrollBarLeft(); void showScrollBarRight(); void scrollFullPage(); void scrollHalfPage(); // keyboard page void editKeyBinding(); void newKeyBinding(); void keyBindingSelected(); void removeKeyBinding(); + void resetKeyBindings(); // mouse page void toggleUnderlineFiles(bool enable); void toggleUnderlineLinks(bool); void toggleOpenLinksByDirectClick(bool); void toggleCtrlRequiredForDrag(bool); void toggleDropUrlsAsText(bool); void toggleCopyTextToClipboard(bool); void toggleCopyTextAsHTML(bool); void toggleTrimLeadingSpacesInSelectedText(bool); void toggleTrimTrailingSpacesInSelectedText(bool); void pasteFromX11Selection(); void pasteFromClipboard(); void toggleAlternateScrolling(bool enable); void TripleClickModeChanged(int); void wordCharactersChanged(const QString &); // advanced page void toggleBlinkingText(bool); void toggleFlowControl(bool); void togglebidiRendering(bool); void lineSpacingChanged(int); void toggleBlinkingCursor(bool); void updateUrlHintsModifier(bool); void setCursorShape(int); void autoCursorColor(); void customCursorColor(); void customCursorColorChanged(const QColor &); void setDefaultCodec(QTextCodec *); // apply the first previewed changes stored up by delayedPreview() void delayedPreviewActivate(); private: Q_DISABLE_COPY(EditProfileDialog) // initialize various pages of the dialog void setupGeneralPage(const Profile::Ptr profile); void setupTabsPage(const Profile::Ptr profile); void setupAppearancePage(const Profile::Ptr profile); void setupKeyboardPage(const Profile::Ptr profile); void setupScrollingPage(const Profile::Ptr profile); void setupAdvancedPage(const Profile::Ptr profile); void setupMousePage(const Profile::Ptr info); // Returns the name of the colorScheme used in the current profile const QString currentColorSchemeName() const; // select @p selectedColorSchemeName after the changes are saved // in the colorScheme editor void updateColorSchemeList(const QString &selectedColorSchemeName = QString()); void updateColorSchemeButtons(); - void updateKeyBindingsList(bool selectCurrentTranslator = false); + + // Convenience method + KeyboardTranslatorManager *_keyManager = KeyboardTranslatorManager::instance(); + + // Updates the key bindings list widget on the Keyboard tab and selects + // @p selectTranslatorName + void updateKeyBindingsList(const QString &selectTranslatorName = QString()); void updateKeyBindingsButtons(); + void showKeyBindingEditor(bool isNewTranslator); void showColorSchemeEditor(bool isNewScheme); void closeColorSchemeEditor(); - void showKeyBindingEditor(bool isNewTranslator); void preview(int property, const QVariant &value); void delayedPreview(int property, const QVariant &value); void unpreview(int property); void unpreviewAll(); void enableIfNonEmptySelection(QWidget *widget, QItemSelectionModel *selectionModel); void updateCaption(const Profile::Ptr profile); void updateTransparencyWarning(); // Update _tempProfile in a way of respecting the apply button. // When used with some previewed property, this method should // always come after the preview operation. void updateTempProfileProperty(Profile::Property, const QVariant &value); // helper method for creating an empty & hidden profile and assigning // it to _tempProfile. void createTempProfile(); // Enable or disable apply button, used only within // updateTempProfileProperty(). void updateButtonApply(); static QString groupProfileNames(const ProfileGroup::Ptr group, int maxLength = -1); struct RadioOption { QAbstractButton *button; int value; const char *slot; }; void setupRadio(RadioOption *possibilities, int actual); struct BooleanOption { QAbstractButton *button; Profile::Property property; const char *slot; }; void setupCheckBoxes(BooleanOption *options, const Profile::Ptr profile); // returns false if: // - the profile name is empty // - the name matches the name of an already existing profile // - the existing profile config file is read-only // otherwise returns true. bool isValidProfileName(); Ui::EditProfileDialog *_ui; Profile::Ptr _tempProfile; Profile::Ptr _profile; // keeps track of pages which need to be updated to match the current // profile. all elements in this vector are set to true when the // profile is changed and individual elements are set to false // after an update by a call to ensurePageLoaded() QVector _pageNeedsUpdate; QHash _previewedProperties; QHash _delayedPreviewProperties; QTimer *_delayedPreviewTimer; ColorSchemeEditor *_colorDialog; QDialogButtonBox *mButtonBox; }; /** * A delegate which can display and edit color schemes in a view. */ class ColorSchemeViewDelegate : public QAbstractItemDelegate { Q_OBJECT public: explicit ColorSchemeViewDelegate(QObject *parent = nullptr); // reimplemented void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE; }; } #endif // EDITPROFILEDIALOG_H diff --git a/src/EditProfileDialog.ui b/src/EditProfileDialog.ui index 08bee95d..10441365 100644 --- a/src/EditProfileDialog.ui +++ b/src/EditProfileDialog.ui @@ -1,1561 +1,1571 @@ EditProfileDialog 0 0 594 536 0 0 0 0 0 0 0 true General General true 0 0 64 64 0 0 Select the icon displayed on tabs using this profile 48 48 Qt::Horizontal QSizePolicy::Fixed 20 20 Profile name: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter A descriptive name for the profile Command: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter The command to execute when new terminal sessions are created using this profile Qt::LeftToRight Initial directory: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter The initial working directory for new terminal sessions using this profile Qt::LeftToRight Choose the initial directory ... Start in same directory as current tab Environment: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Edit the list of environment variables and associated values Edit... Qt::Horizontal 81 20 Terminal Size true Columns 1 500 Rows 1 Qt::Horizontal QSizePolicy::Fixed 40 20 This will not alter any open windows. Qt::Horizontal 81 20 Qt::Horizontal QSizePolicy::Fixed 40 20 Configure Konsole->General->Use current window size on next startup must be disabled for these entries to work. true Window true Show terminal size in columns and lines in the center of window after resizing Show hint for terminal size after resizing Qt::Vertical 20 20 Tabs Tab Titles true Tab Monitoring true Threshold for continuous silence: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter The threshold for continuous silence to be detected by Konsole 1 3600 Qt::Horizontal 20 20 Qt::Vertical 20 10 Appearance 0 1 Color Scheme && Background true Reset the selected color scheme settings to the default values Defaults Delete the selected color scheme Remove QAbstractItemView::ScrollPerPixel Get New... Create a new color scheme based upon the selected scheme New... Qt::Vertical 20 20 Edit the selected color scheme Edit... QFrame::StyledPanel QFrame::Raised Font true Preview: 1 0 KSqueezedTextLabel Qt::ElideRight Text size: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 1 4.000000000000000 999.000000000000000 1.000000000000000 Select the font used in this profile Select Font... Show all fonts instead of the monospaced fonts Show All Fonts Qt::Horizontal 40 20 Smooth fonts Draw intense colors in bold font Use the selected font for line characters instead of the builtin code Use line characters contained in font Scrolling Scrollback true Scroll Bar true 0 0 Hide the scroll bar Hide 0 0 Show the scroll bar on the left side of the terminal window Show on left side 0 0 Show the scroll bar on the right side of the terminal window Show on right side Scroll Page Up/Down Amount true Scroll the page the half height of window Half Page Height Scroll the page the full height of window Full Page Height Qt::Vertical 20 1 Keyboard Key Bindings true Key bindings control how combinations of keystrokes in the terminal window are converted into the stream of characters that is then sent to the current terminal program. For more information on how to customize the key bindings check the Konsole Handbook. true 32 32 - Create a new key bindings list based upon the selected bindings + Create a new key bindings scheme based upon the selected bindings New... - Edit the selected key bindings list + Edit the selected key bindings scheme Edit... - Delete the selected key bindings list + Delete the selected key bindings scheme Remove Qt::Vertical 20 20 + + + + Reset the selected key bindings scheme to its default values + + + Defaults + + + Mouse Select Text true Characters considered part of a word when double clicking: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter true Characters which are considered part of a word when double-clicking to select whole words in the terminal Triple-click select: Which part of current line should be selected with triple click . The whole current line From mouse position to the end of line Copy && Paste true Trim leading spaces in selected text, useful in some instances Trim leading spaces Trim trailing spaces in selected text, useful in some instances Trim trailing spaces Mouse middle button: Paste from clipboard Paste from selection Automatically copy selected text into clipboard Copy on select Copy text as HTML (including formatting, font faces, colors... etc) Copy text as HTML Miscellaneous true Text recognized as a file will be underlined when hovered by the mouse pointer. Underline files Selected text will require control key plus click to drag. Require Ctrl key for drag && drop Always paste dropped files and URLs as text without offering move, copy and link actions. Disable drag && drop menu for files && URLs Mouse scroll wheel will emulate up/down key presses in programs that use the Alternate Screen buffer (e.g. less) Enable Alternate Screen buffer scrolling Qt::Horizontal QSizePolicy::Fixed 10 20 false 0 0 Text recognized as a file, link or an email address can be opened by direct mouse click. Open by direct click Text recognized as a link or an email address will be underlined when hovered by the mouse pointer. Underline links Pressing Ctrl+scrollwheel will increase/decrease the text size. Allow Ctrl+scrollwheel to zoom text size Advanced Terminal Features true Show URL hints when these keys are pressed: Qt::Horizontal QSizePolicy::Fixed 20 20 Shift Control Alt Meta 0 0 Allow terminal programs to create blinking sections of text Allow blinking text 0 0 Allow the output to be suspended by pressing Ctrl+S Enable flow control using Ctrl+S, Ctrl+Q 0 0 Enable Bi-Directional display on terminals (valid for Arabic, Farsi or Hebrew only) Enable Bi-Directional text rendering Line Spacing: The number of pixels between two lines 0 5 Qt::Horizontal 40 20 Cursor true 0 0 Make the cursor blink regularly Blinking cursor Cursor shape: Change the shape of the cursor Block I-Beam Underline Qt::Horizontal 40 20 0 0 Set the cursor to match the color of the character underneath it. Set cursor color to match current character 0 0 Use a custom, fixed color for the cursor Custom cursor color: 0 0 Select the color used to draw the cursor Qt::Horizontal 40 20 Encoding true Default character encoding: 0 0 DEFAULTENCODING Select Qt::Vertical 20 20 KComboBox QComboBox
kcombobox.h
KNS3::Button QPushButton
KNS3/Button
KPluralHandlingSpinBox QSpinBox
KPluralHandlingSpinBox
KColorButton QPushButton
kcolorbutton.h
KSqueezedTextLabel QLabel
ksqueezedtextlabel.h
KMessageWidget QFrame
kmessagewidget.h
1
Konsole::RenameTabWidget QWidget
RenameTabWidget.h
Konsole::HistorySizeWidget QWidget
HistorySizeWidget.h
diff --git a/src/KeyBindingEditor.cpp b/src/KeyBindingEditor.cpp index f5b88227..2844c58a 100644 --- a/src/KeyBindingEditor.cpp +++ b/src/KeyBindingEditor.cpp @@ -1,242 +1,337 @@ /* Copyright 2008 by Robert Knight 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 "KeyBindingEditor.h" // Qt +#include +#include #include #include // KDE #include +#include // Konsole #include "ui_KeyBindingEditor.h" +#include "EditProfileDialog.h" #include "KeyboardTranslator.h" +#include "KeyboardTranslatorManager.h" using namespace Konsole; KeyBindingEditor::KeyBindingEditor(QWidget *parent) : - QWidget(parent), + QDialog(parent), _ui(nullptr), - _translator(new KeyboardTranslator(QString())) + _translator(new KeyboardTranslator(QString())), + _isNewTranslator(false) { + auto layout = new QVBoxLayout; + + auto mainWidget = new QWidget(this); + layout->addWidget(mainWidget); + + auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + buttonBox->button(QDialogButtonBox::Cancel)->setDefault(true); + layout->addWidget(buttonBox); + + setLayout(layout); + + connect(buttonBox, &QDialogButtonBox::accepted, this, &Konsole::KeyBindingEditor::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + setAttribute(Qt::WA_DeleteOnClose); + _ui = new Ui::KeyBindingEditor(); - _ui->setupUi(this); + _ui->setupUi(mainWidget); // description edit + _ui->descriptionEdit->setPlaceholderText(i18nc("@label:textbox", "Enter descriptive label")); connect(_ui->descriptionEdit, &QLineEdit::textChanged, this, &Konsole::KeyBindingEditor::setTranslatorDescription); + // filter edit + connect(_ui->filterEdit, &QLineEdit::textChanged, this, + &Konsole::KeyBindingEditor::filterRows); // key bindings table _ui->keyBindingTable->setColumnCount(2); QStringList labels; labels << i18n("Key Combination") << i18n("Output"); _ui->keyBindingTable->setHorizontalHeaderLabels(labels); - _ui->keyBindingTable->horizontalHeader()->setStretchLastSection(true); - - // see also the sizes set in EditProfileDialog::showKeyBindingEditor() - _ui->keyBindingTable->setColumnWidth(0, 300); + _ui->keyBindingTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); _ui->keyBindingTable->verticalHeader()->hide(); _ui->keyBindingTable->setSelectionBehavior(QAbstractItemView::SelectRows); // add and remove buttons _ui->addEntryButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); _ui->removeEntryButton->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); connect(_ui->removeEntryButton, &QPushButton::clicked, this, &Konsole::KeyBindingEditor::removeSelectedEntry); connect(_ui->addEntryButton, &QPushButton::clicked, this, &Konsole::KeyBindingEditor::addNewEntry); // test area _ui->testAreaInputEdit->installEventFilter(this); } KeyBindingEditor::~KeyBindingEditor() { delete _ui; delete _translator; } +void KeyBindingEditor::filterRows(const QString &text) +{ + const int rows = _ui->keyBindingTable->rowCount(); + + QList matchedRows; + + for (QTableWidgetItem *matchedItem : _ui->keyBindingTable->findItems(text, Qt::MatchContains)) { + matchedRows.append(matchedItem->row()); + } + + for (int i = 0; i < rows; i++) { + if (matchedRows.contains(i) && _ui->keyBindingTable->isRowHidden(i)) { + _ui->keyBindingTable->showRow(i); + } else if (!matchedRows.contains(i)) { + _ui->keyBindingTable->hideRow(i); + } + } +} + void KeyBindingEditor::removeSelectedEntry() { QList uniqueList; foreach (QTableWidgetItem *item, _ui->keyBindingTable->selectedItems()) { if (item->column() == 1) { //Select item at the first column item = _ui->keyBindingTable->item(item->row(), 0); } if (!uniqueList.contains(item)) { uniqueList.append(item); } } foreach (QTableWidgetItem *item, uniqueList) { // get the first item in the row which has the entry KeyboardTranslator::Entry existing = item->data(Qt::UserRole). value(); _translator->removeEntry(existing); _ui->keyBindingTable->removeRow(item->row()); } } void KeyBindingEditor::addNewEntry() { _ui->keyBindingTable->insertRow(_ui->keyBindingTable->rowCount()); int newRowCount = _ui->keyBindingTable->rowCount(); // block signals here to avoid triggering bindingTableItemChanged() slot call _ui->keyBindingTable->blockSignals(true); _ui->keyBindingTable->setItem(newRowCount - 1, 0, new QTableWidgetItem()); _ui->keyBindingTable->setItem(newRowCount - 1, 1, new QTableWidgetItem()); _ui->keyBindingTable->blockSignals(false); // make sure user can see new row _ui->keyBindingTable->scrollToItem(_ui->keyBindingTable->item(newRowCount - 1, 0)); } bool KeyBindingEditor::eventFilter(QObject *watched, QEvent *event) { if (watched == _ui->testAreaInputEdit) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); // The state here is currently set to the state that a newly started // terminal in Konsole will be in ( which is also the same as the // state just after a reset ), this has 'ANSI' turned on and all other // states off. // // TODO: It may be useful to be able to specify the state in the 'test input' // area, but preferably not in a way which clutters the UI with lots of // checkboxes. // const KeyboardTranslator::States states = KeyboardTranslator::AnsiState; KeyboardTranslator::Entry entry = _translator->findEntry(keyEvent->key(), keyEvent->modifiers(), states); if (!entry.isNull()) { _ui->testAreaInputEdit->setText(entry.conditionToString()); _ui->testAreaOutputEdit->setText(entry.resultToString(true, keyEvent->modifiers())); } else { _ui->testAreaInputEdit->setText(keyEvent->text()); _ui->testAreaOutputEdit->setText(keyEvent->text()); } keyEvent->accept(); return true; } } return false; } void KeyBindingEditor::setDescription(const QString &description) { _ui->descriptionEdit->setText(description); setTranslatorDescription(description); } void KeyBindingEditor::setTranslatorDescription(const QString &description) { if (_translator != nullptr) { _translator->setDescription(description); } } QString KeyBindingEditor::description() const { return _ui->descriptionEdit->text(); } -void KeyBindingEditor::setup(const KeyboardTranslator *translator) +void KeyBindingEditor::setup(const KeyboardTranslator *translator, + const QString ¤tProfileTranslator, bool isNewTranslator) { delete _translator; + _isNewTranslator = isNewTranslator; + + _currentProfileTranslator = currentProfileTranslator; + _translator = new KeyboardTranslator(*translator); - // setup description edit + // setup description edit line _ui->descriptionEdit->setClearButtonEnabled(true); - _ui->descriptionEdit->setText(translator->description()); + // setup filter edit line + _ui->filterEdit->setClearButtonEnabled(true); + + if (_isNewTranslator) { + setDescription(i18n("New Key Binding List")); + setWindowTitle(i18n("New Key Binding List")); + } else { + _ui->descriptionEdit->setText(translator->description()); + setWindowTitle(i18n("Edit Key Binding List")); + } // setup key binding table setupKeyBindingTable(translator); } KeyboardTranslator *KeyBindingEditor::translator() const { return _translator; } void KeyBindingEditor::bindingTableItemChanged(QTableWidgetItem *item) { QTableWidgetItem *key = _ui->keyBindingTable->item(item->row(), 0); KeyboardTranslator::Entry existing = key->data(Qt::UserRole).value(); QString condition = key->text(); QString result = _ui->keyBindingTable->item(item->row(), 1)->text(); KeyboardTranslator::Entry entry = KeyboardTranslatorReader::createEntry(condition, result); _translator->replaceEntry(existing, entry); // block signals to prevent this slot from being called repeatedly _ui->keyBindingTable->blockSignals(true); key->setData(Qt::UserRole, QVariant::fromValue(entry)); _ui->keyBindingTable->blockSignals(false); } void KeyBindingEditor::setupKeyBindingTable(const KeyboardTranslator *translator) { disconnect(_ui->keyBindingTable, &QTableWidget::itemChanged, this, &Konsole::KeyBindingEditor::bindingTableItemChanged); QList entries = translator->entries(); _ui->keyBindingTable->setRowCount(entries.count()); for (int row = 0; row < entries.count(); row++) { const KeyboardTranslator::Entry &entry = entries.at(row); QTableWidgetItem *keyItem = new QTableWidgetItem(entry.conditionToString()); keyItem->setData(Qt::UserRole, QVariant::fromValue(entry)); QTableWidgetItem *textItem = new QTableWidgetItem(entry.resultToString()); _ui->keyBindingTable->setItem(row, 0, keyItem); _ui->keyBindingTable->setItem(row, 1, textItem); } _ui->keyBindingTable->sortItems(0); connect(_ui->keyBindingTable, &QTableWidget::itemChanged, this, &Konsole::KeyBindingEditor::bindingTableItemChanged); } + +void KeyBindingEditor::accept() +{ + if (_translator == nullptr) { + return; + } + + const auto newTranslator = new KeyboardTranslator(*_translator); + + if (newTranslator->description().isEmpty()) { + KMessageBox::sorry(this, i18n("A key bindings scheme cannot be saved with an empty description.")); + return; + } + + if (_isNewTranslator) { + newTranslator->setName(newTranslator->description()); + } + + KeyboardTranslatorManager::instance()->addTranslator(newTranslator); + + const QString ¤tTranslatorName = newTranslator->name(); + + emit updateKeyBindingsListRequest(currentTranslatorName); + + if (currentTranslatorName == _currentProfileTranslator) { + emit updateTempProfileKeyBindingsRequest(Profile::KeyBindings, currentTranslatorName); + } + + QDialog::accept(); +} + +QSize KeyBindingEditor::sizeHint() const +{ + const auto parent = parentWidget(); + if (parent != nullptr) { + return QSize(parent->width() * 0.9, parent->height() * 0.95); + } + + return QSize(); +} diff --git a/src/KeyBindingEditor.h b/src/KeyBindingEditor.h index bd5f516d..a27b28c3 100644 --- a/src/KeyBindingEditor.h +++ b/src/KeyBindingEditor.h @@ -1,103 +1,149 @@ /* Copyright 2007-2008 by Robert Knight 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. */ #ifndef KEYBINDINGEDITOR_H #define KEYBINDINGEDITOR_H // Qt -#include +#include + +// Konsole +#include "Profile.h" class QTableWidgetItem; namespace Ui { class KeyBindingEditor; } namespace Konsole { class KeyboardTranslator; /** - * A dialog which allows the user to edit a key bindings list + * A dialog which allows the user to edit a key bindings scheme * which maps between key combinations input by the user and * the character sequence sent to the terminal when those * combinations are pressed. * * The dialog can be initialized with the settings of an - * existing key bindings list using the setup() method. + * existing key bindings scheme using the setup() method. * * The dialog creates a copy of the supplied keyboard translator * to which any changes are applied. The modified translator * can be retrieved using the translator() method. */ -class KeyBindingEditor : public QWidget +class KeyBindingEditor : public QDialog { Q_OBJECT public: /** Constructs a new key bindings editor with the specified parent. */ explicit KeyBindingEditor(QWidget *parent = nullptr); ~KeyBindingEditor() Q_DECL_OVERRIDE; /** * Initializes the dialog with the bindings and other settings * from the specified @p translator. + * @p currentProfileTranslator the name of the translator set in the + * current profile + * @p isNewTranslator specifies whether the translator being edited + * is an already existing one or a newly created + * one, defaults to false. */ - void setup(const KeyboardTranslator *translator); + void setup(const KeyboardTranslator *translator, const QString ¤tProfileTranslator, + bool isNewTranslator = false); /** * Returns the modified translator describing the changes to the bindings * and other settings which the user made. */ KeyboardTranslator *translator() const; /** * Sets the text of the editor's description field. */ void setDescription(const QString &description); /** * Returns the text of the editor's description field. */ QString description() const; // reimplemented to handle test area input bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE; +Q_SIGNALS: + /** + * Emitted when the user clicks the OK button to save the changes. This + * signal is connected to EditProfileDialog::updateKeyBindingsList() + * to update the key bindings list on the Keyboard tab in the Edit + * Profile dialog. + * @p translatorName is the translator that has just been edited + */ + void updateKeyBindingsListRequest(const QString &translatorName); + + /** + * Emitted when the user clicks the OK button to save the changes to + * the translator that's set in the current profile; this signal is + * connected to EditProfileDialog::updateTempProfileProperty() to + * request applying the changes to the _tempProfile. + * @p newTranslatorName is the name of the translator, that has just + * been edited/saved, and which is also the translator + * set in the current Profile + */ + void updateTempProfileKeyBindingsRequest(Profile::Property, const QString &newTranslatorName); + private Q_SLOTS: + // reimplemented + void accept() Q_DECL_OVERRIDE; + void setTranslatorDescription(const QString &description); void bindingTableItemChanged(QTableWidgetItem *item); void removeSelectedEntry(); void addNewEntry(); private: Q_DISABLE_COPY(KeyBindingEditor) void setupKeyBindingTable(const KeyboardTranslator *translator); Ui::KeyBindingEditor *_ui; // translator to which modifications are made as the user makes // changes in the UI. // this is initialized as a copy of the translator specified // when setup() is called KeyboardTranslator *_translator; + + // Show only the rows that match the text entered by the user in the + // filter search box + void filterRows(const QString &text); + + bool _isNewTranslator; + + // The translator set in the current profile + QString _currentProfileTranslator; + + // Sets the size hint of the dialog to be slightly smaller than the + // size of EditProfileDialog + QSize sizeHint() const Q_DECL_OVERRIDE; }; } #endif //KEYBINDINGEDITOR_H diff --git a/src/KeyBindingEditor.ui b/src/KeyBindingEditor.ui index 0d58f2db..e6426bb7 100644 --- a/src/KeyBindingEditor.ui +++ b/src/KeyBindingEditor.ui @@ -1,139 +1,149 @@ KeyBindingEditor 0 0 374 530 0 0 0 0 - - + + Description: - + + + + + Filter: + + + + + + 0 1 Add Remove Qt::Vertical 20 16 Test Area true 0 0 0 0 Input: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Output: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter true diff --git a/src/KeyboardTranslatorManager.cpp b/src/KeyboardTranslatorManager.cpp index 15c4bde5..040145e6 100644 --- a/src/KeyboardTranslatorManager.cpp +++ b/src/KeyboardTranslatorManager.cpp @@ -1,214 +1,227 @@ /* This source file is part of Konsole, a terminal emulator. Copyright 2007-2008 by Robert Knight 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 "KeyboardTranslatorManager.h" #include "konsoledebug.h" // Qt #include #include #include #include using namespace Konsole; KeyboardTranslatorManager::KeyboardTranslatorManager() : _haveLoadedAll(false), _fallbackTranslator(nullptr), _translators(QHash()) { _fallbackTranslator = new FallbackKeyboardTranslator(); } KeyboardTranslatorManager::~KeyboardTranslatorManager() { qDeleteAll(_translators); delete _fallbackTranslator; } Q_GLOBAL_STATIC(KeyboardTranslatorManager, theKeyboardTranslatorManager) KeyboardTranslatorManager* KeyboardTranslatorManager::instance() { return theKeyboardTranslatorManager; } void KeyboardTranslatorManager::addTranslator(KeyboardTranslator *translator) { _translators.insert(translator->name(), translator); if (!saveTranslator(translator)) { qCDebug(KonsoleDebug) << "Unable to save translator" << translator->name() << "to disk."; } } bool KeyboardTranslatorManager::deleteTranslator(const QString &name) { Q_ASSERT(_translators.contains(name)); // locate and delete QString path = findTranslatorPath(name); if (QFile::remove(path)) { _translators.remove(name); return true; } else { qCDebug(KonsoleDebug) << "Failed to remove translator - " << path; return false; } } -QString KeyboardTranslatorManager::findTranslatorPath(const QString &name) +bool KeyboardTranslatorManager::isTranslatorDeletable(const QString &name) const +{ + const QString &dir = QFileInfo(findTranslatorPath(name)).path(); + return QFileInfo(dir).isWritable(); +} + +bool KeyboardTranslatorManager::isTranslatorResettable(const QString &name) const +{ + const QStringList &paths = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("konsole/") + name + QStringLiteral(".keytab")); + + return (paths.count() > 1); +} + +const QString KeyboardTranslatorManager::findTranslatorPath(const QString &name) const { return QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("konsole/") + name + QStringLiteral(".keytab")); } void KeyboardTranslatorManager::findTranslators() { QStringList list; const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("konsole"), QStandardPaths::LocateDirectory); list.reserve(dirs.size()); Q_FOREACH (const QString &dir, dirs) { const QStringList fileNames = QDir(dir).entryList(QStringList() << QStringLiteral("*.keytab")); Q_FOREACH (const QString &file, fileNames) { list.append(dir + QLatin1Char('/') + file); } } // add the name of each translator to the list and associated // the name with a null pointer to indicate that the translator // has not yet been loaded from disk foreach (const QString &translatorPath, list) { QString name = QFileInfo(translatorPath).completeBaseName(); if (!_translators.contains(name)) { _translators.insert(name, nullptr); } } _haveLoadedAll = true; } const KeyboardTranslator *KeyboardTranslatorManager::findTranslator(const QString &name) { if (name.isEmpty()) { return defaultTranslator(); } if (_translators.contains(name) && _translators[name] != nullptr) { return _translators[name]; } KeyboardTranslator *translator = loadTranslator(name); if (translator != nullptr) { _translators[name] = translator; } else if (!name.isEmpty()) { qCDebug(KonsoleDebug) << "Unable to load translator" << name; } return translator; } bool KeyboardTranslatorManager::saveTranslator(const KeyboardTranslator *translator) { const QString dir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/konsole/"); QDir().mkpath(dir); const QString path = dir + translator->name() + QStringLiteral(".keytab"); ////qDebug() << "Saving translator to" << path; QFile destination(path); if (!destination.open(QIODevice::WriteOnly | QIODevice::Text)) { qCDebug(KonsoleDebug) << "Unable to save keyboard translation:" << destination.errorString(); return false; } { KeyboardTranslatorWriter writer(&destination); writer.writeHeader(translator->description()); foreach (const KeyboardTranslator::Entry &entry, translator->entries()) { writer.writeEntry(entry); } } destination.close(); return true; } KeyboardTranslator *KeyboardTranslatorManager::loadTranslator(const QString &name) { const QString &path = findTranslatorPath(name); QFile source(path); if (name.isEmpty() || !source.open(QIODevice::ReadOnly | QIODevice::Text)) { return nullptr; } return loadTranslator(&source, name); } KeyboardTranslator *KeyboardTranslatorManager::loadTranslator(QIODevice *source, const QString &name) { auto translator = new KeyboardTranslator(name); KeyboardTranslatorReader reader(source); translator->setDescription(reader.description()); while (reader.hasNextEntry()) { translator->addEntry(reader.nextEntry()); } source->close(); if (!reader.parseError()) { return translator; } else { delete translator; return nullptr; } } const KeyboardTranslator *KeyboardTranslatorManager::defaultTranslator() { // Try to find the default.keytab file if it exists, otherwise // fall back to the internal hard-coded fallback translator const KeyboardTranslator *translator = findTranslator(QStringLiteral("default")); if (translator == nullptr) { translator = _fallbackTranslator; } return translator; } -QStringList KeyboardTranslatorManager::allTranslators() +const QStringList KeyboardTranslatorManager::allTranslators() { if (!_haveLoadedAll) { findTranslators(); } return _translators.keys(); } diff --git a/src/KeyboardTranslatorManager.h b/src/KeyboardTranslatorManager.h index 4582ccd0..39db6a99 100644 --- a/src/KeyboardTranslatorManager.h +++ b/src/KeyboardTranslatorManager.h @@ -1,110 +1,135 @@ /* This source file is part of Konsole, a terminal emulator. Copyright 2007-2008 by Robert Knight 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. */ #ifndef KEYBOARDTRANSLATOR_MANAGER_H #define KEYBOARDTRANSLATOR_MANAGER_H // Qt #include #include // Konsole #include "konsoleprivate_export.h" #include "KeyboardTranslator.h" class QIODevice; namespace Konsole { /** * Manages the keyboard translations available for use by terminal sessions, * see KeyboardTranslator. */ class KeyboardTranslatorManager { public: /** * Constructs a new KeyboardTranslatorManager and loads the list of * available keyboard translations. * * The keyboard translations themselves are not loaded until they are * first requested via a call to findTranslator() */ KeyboardTranslatorManager(); ~KeyboardTranslatorManager(); KeyboardTranslatorManager(const KeyboardTranslatorManager &) = delete; KeyboardTranslatorManager &operator=(const KeyboardTranslatorManager &) = delete; /** * Adds a new translator. If a translator with the same name * already exists, it will be replaced by the new translator. * * TODO: More documentation. */ void addTranslator(KeyboardTranslator *translator); /** * Deletes a translator. Returns true on successful deletion or false otherwise. * * TODO: More documentation */ bool deleteTranslator(const QString &name); + /** + * Checks whether a translator can be deleted or not (by checking if + * the directory containing the .keytab file is writable, because one + * can still delete a file owned by a different user if the directory + * containing it is writable for the current user). + */ + bool isTranslatorDeletable(const QString &name) const; + + /** + * Checks whether a translator can be reset to its default values. + * This is only applicable for translators that exist in two different + * locations: + * - system-wide location which is read-only for the user (typically + * /usr/share/konsole/ on Linux) + * - writable user-specific location under the user's home directory + * (typically ~/.local/share/konsole on Linux) + * + * Reseting here basically means it deletes the translator from the + * location under the user's home directory, then "reloads" it from + * the system-wide location. + */ + bool isTranslatorResettable(const QString &name) const; + /** Returns the default translator for Konsole. */ const KeyboardTranslator *defaultTranslator(); /** * Returns the keyboard translator with the given name or 0 if no translator * with that name exists. * * The first time that a translator with a particular name is requested, * the on-disk .keytab file is loaded and parsed. */ const KeyboardTranslator *findTranslator(const QString &name); /** * Returns a list of the names of available keyboard translators. * * The first time this is called, a search for available * translators is started. */ - QStringList allTranslators(); + const QStringList allTranslators(); /** Returns the global KeyboardTranslatorManager instance. */ static KeyboardTranslatorManager *instance(); + /** Returns the translator path */ + const QString findTranslatorPath(const QString &name) const; + private: void findTranslators(); // locate all available translators // loads the translator with the given name KeyboardTranslator *loadTranslator(const QString &name); KeyboardTranslator *loadTranslator(QIODevice *source, const QString &name); bool saveTranslator(const KeyboardTranslator *translator); - QString findTranslatorPath(const QString &name); bool _haveLoadedAll; const KeyboardTranslator *_fallbackTranslator; QHash _translators; }; } #endif // KEYBOARDTRANSLATOR_MANAGER_H