diff --git a/src/EditProfileDialog.cpp b/src/EditProfileDialog.cpp index 5fff7674..c2cca911 100644 --- a/src/EditProfileDialog.cpp +++ b/src/EditProfileDialog.cpp @@ -1,1364 +1,1364 @@ /* 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 "EditProfileDialog.h" // Standard #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE #include #include #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" using namespace Konsole; EditProfileDialog::EditProfileDialog(QWidget* aParent) : QDialog(aParent) , _delayedPreviewTimer(new QTimer(this)) , _colorDialog(0) { setWindowTitle(i18n("Edit Profile")); mButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel|QDialogButtonBox::Apply); QWidget *mainWidget = new QWidget(this); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(mainWidget); QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); 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::save); 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() { Q_ASSERT(_profile); Q_ASSERT(_tempProfile); 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.

")); return; } save(); unpreviewAll(); QDialog::accept(); } 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 += ','; // 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; } 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->emptyNameWarningWidget->setWordWrap(false); _ui->emptyNameWarningWidget->setCloseButtonVisible(false); _ui->emptyNameWarningWidget->setMessageType(KMessageWidget::Warning); ProfileGroup::Ptr group = profile->asGroup(); if (!group || group->profiles().count() < 2) { _ui->profileNameEdit->setText(profile->name()); _ui->profileNameEdit->setClearButtonEnabled(true); _ui->emptyNameWarningWidget->setVisible(profile->name().isEmpty()); _ui->emptyNameWarningWidget->setText(i18n("Profile name is empty.")); } else { _ui->profileNameEdit->setText(groupProfileNames(group, -1)); _ui->profileNameEdit->setEnabled(false); _ui->profileNameLabel->setEnabled(false); _ui->emptyNameWarningWidget->setVisible(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 = profile->environment(); QString text = QInputDialog::getMultiLineText(this, i18n("Edit Environment"), i18n("One environment variable per line"), currentEnvironment.join(QStringLiteral("\n")), &ok); if (ok && !text.isEmpty()) { QStringList newEnvironment = text.split('\n'); 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& text) { _ui->emptyNameWarningWidget->setVisible(text.isEmpty()); updateTempProfileProperty(Profile::Name, text); updateTempProfileProperty(Profile::UntranslatedName, text); 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) { ColorSchemeViewDelegate* 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->editColorSchemeButton->setEnabled(false); _ui->removeColorSchemeButton->setEnabled(false); // setup color list updateColorSchemeList(true); _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); // 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, "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.", "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::updateColorSchemeList(bool selectCurrentScheme) { if (!_ui->colorSchemeList->model()) _ui->colorSchemeList->setModel(new QStandardItemModel(this)); const QString& name = lookupProfile()->colorScheme(); const ColorScheme* currentScheme = ColorSchemeManager::instance()->findColorScheme(name); QStandardItemModel* model = qobject_cast(_ui->colorSchemeList->model()); Q_ASSERT(model); model->clear(); QStandardItem* selectedItem = 0; 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 (currentScheme == scheme) selectedItem = item; model->appendRow(item); } model->sort(0); if (selectCurrentScheme && selectedItem) { _ui->colorSchemeList->updateGeometry(); _ui->colorSchemeList->selectionModel()->setCurrentIndex(selectedItem->index() , QItemSelectionModel::Select); // update transparency warning label updateTransparencyWarning(); } } void EditProfileDialog::updateKeyBindingsList(bool selectCurrentTranslator) { if (!_ui->keyBindingList->model()) _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 = 0; QStringList translatorNames = keyManager->allTranslators(); foreach(const QString& translatorName, translatorNames) { const KeyboardTranslator* translator = keyManager->findTranslator(translatorName); if (!translator) continue; QStandardItem* item = new QStandardItem(translator->description()); item->setEditable(false); item->setData(QVariant::fromValue(translator), Qt::UserRole + 1); item->setData(QVariant::fromValue(_profile->font()), Qt::UserRole + 2); item->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-keyboard"))); if (translator == currentTranslator) selectedItem = item; model->appendRow(item); } model->sort(0); if (selectCurrentTranslator && selectedItem) { _ui->keyBindingList->selectionModel()->setCurrentIndex(selectedItem->index() , QItemSelectionModel::Select); } } bool EditProfileDialog::eventFilter(QObject* watched , QEvent* aEvent) { if (watched == _ui->colorSchemeList && aEvent->type() == QEvent::Leave) { if (_tempProfile->isPropertySet(Profile::ColorScheme)) preview(Profile::ColorScheme, _tempProfile->colorScheme()); else unpreview(Profile::ColorScheme); } if (watched == _ui->fontPreviewLabel && aEvent->type() == QEvent::FontChange) { const QFont& labelFont = _ui->fontPreviewLabel->font(); _ui->fontPreviewLabel->setText(i18n("%1", labelFont.family())); } return QDialog::eventFilter(watched, aEvent); } void EditProfileDialog::unpreviewAll() { _delayedPreviewTimer->stop(); _delayedPreviewProperties.clear(); QHash map; QHashIterator iter(_previewedProperties); while (iter.hasNext()) { iter.next(); map.insert((Profile::Property)iter.key(), iter.value()); } // undo any preview changes if (!map.isEmpty()) ProfileManager::instance()->changeProfile(_profile, map, false); } void EditProfileDialog::unpreview(int aProperty) { _delayedPreviewProperties.remove(aProperty); if (!_previewedProperties.contains(aProperty)) return; QHash map; map.insert((Profile::Property)aProperty, _previewedProperties[aProperty]); ProfileManager::instance()->changeProfile(_profile, map, false); _previewedProperties.remove(aProperty); } void EditProfileDialog::delayedPreview(int aProperty , const QVariant& value) { _delayedPreviewProperties.insert(aProperty, 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 aProperty , const QVariant& value) { QHash map; map.insert((Profile::Property)aProperty, value); _delayedPreviewProperties.remove(aProperty); 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((Profile::Property)aProperty).isNull()) return; if (!_previewedProperties.contains(aProperty)) { _previewedProperties.insert(aProperty , original->property((Profile::Property)aProperty)); } // 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()) { const QString& name = selected.first().data(Qt::UserRole + 1).value()->name(); if (ColorSchemeManager::instance()->deleteColorScheme(name)) _ui->colorSchemeList->model()->removeRow(selected.first().row()); } } void EditProfileDialog::showColorSchemeEditor(bool isNewScheme) { // Finding selected ColorScheme QModelIndexList selected = _ui->colorSchemeList->selectionModel()->selectedIndexes(); QAbstractItemModel* model = _ui->colorSchemeList->model(); const ColorScheme* colors = 0; 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) { 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) { _colorDialog->close(); delete _colorDialog; } } void EditProfileDialog::newColorScheme() { showColorSchemeEditor(true); } void EditProfileDialog::editColorScheme() { showColorSchemeEditor(false); } void EditProfileDialog::saveColorScheme(const ColorScheme& scheme, bool isNewScheme) { ColorScheme* 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); updateColorSchemeList(true); 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) { updateTempProfileProperty(Profile::ColorScheme, colors->name()); previewColorScheme(selected.first()); updateTransparencyWarning(); } } updateColorSchemeButtons(); } void EditProfileDialog::updateColorSchemeButtons() { enableIfNonEmptySelection(_ui->editColorSchemeButton, _ui->colorSchemeList->selectionModel()); enableIfNonEmptySelection(_ui->removeColorSchemeButton, _ui->colorSchemeList->selectionModel()); } void EditProfileDialog::updateKeyBindingsButtons() { enableIfNonEmptySelection(_ui->editKeyBindingsButton, _ui->keyBindingList->selectionModel()); enableIfNonEmptySelection(_ui->removeKeyBindingsButton, _ui->keyBindingList->selectionModel()); } 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 aProperty, const QVariant & value) { _tempProfile->setProperty(aProperty, value); updateButtonApply(); } void EditProfileDialog::updateButtonApply() { bool userModified = false; QHashIterator iter(_tempProfile->setProperties()); while (iter.hasNext()) { iter.next(); Profile::Property aProperty = iter.key(); QVariant value = iter.value(); // for previewed property if (_previewedProperties.contains(static_cast(aProperty))) { if (value != _previewedProperties.value(static_cast(aProperty))) { userModified = true; break; } // for not-previewed property } else if ((value != _profile->property(aProperty))) { userModified = true; break; } } mButtonBox->button(QDialogButtonBox::Apply)->setEnabled(userModified); } void EditProfileDialog::setupKeyboardPage(const Profile::Ptr /* profile */) { // setup translator list updateKeyBindingsList(true); connect(_ui->keyBindingList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Konsole::EditProfileDialog::keyBindingSelected); connect(_ui->newKeyBindingsButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::newKeyBinding); updateKeyBindingsButtons(); connect(_ui->editKeyBindingsButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::editKeyBinding); connect(_ui->removeKeyBindingsButton, &QPushButton::clicked, this, &Konsole::EditProfileDialog::removeKeyBinding); } 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) { 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 = 0; if (!selected.isEmpty()) translator = model->data(selected.first(), Qt::UserRole + 1).value(); else translator = KeyboardTranslatorManager::instance()->defaultTranslator(); Q_ASSERT(translator); QPointer dialog = new QDialog(this); QDialogButtonBox *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")); KeyBindingEditor* editor = new KeyBindingEditor; if (translator) editor->setup(translator); if (isNewTranslator) editor->setDescription(i18n("New Key Binding List")); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(editor); layout->addWidget(buttonBox); dialog->setLayout(layout); if (dialog->exec() == QDialog::Accepted) { KeyboardTranslator* newTranslator = new KeyboardTranslator(*editor->translator()); if (isNewTranslator) newTranslator->setName(newTranslator->description()); KeyboardTranslatorManager::instance()->addTranslator(newTranslator); updateKeyBindingsList(); const QString& currentTranslator = lookupProfile() ->property(Profile::KeyBindings); if (newTranslator->name() == currentTranslator) { updateTempProfileProperty(Profile::KeyBindings, newTranslator->name()); } } delete dialog; } void EditProfileDialog::newKeyBinding() { showKeyBindingEditor(true); } void EditProfileDialog::editKeyBinding() { showKeyBindingEditor(false); } void EditProfileDialog::setupCheckBoxes(BooleanOption* options , const Profile::Ptr profile) { while (options->button != 0) { 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 != 0) { 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())}, {0, 0, 0} }; 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())}, {0, 0, 0} }; 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->copyTextToClipboardButton , Profile::AutoCopySelectedText, SLOT(toggleCopyTextToClipboard(bool)) }, { _ui->trimTrailingSpacesButton , Profile::TrimTrailingSpacesInSelectedText, SLOT(toggleTrimTrailingSpacesInSelectedText(bool)) }, { _ui->openLinksByDirectClickButton , Profile::OpenLinksByDirectClickEnabled, SLOT(toggleOpenLinksByDirectClick(bool)) }, { _ui->dropUrlsAsText , Profile::DropUrlsAsText, SLOT(toggleDropUrlsAsText(bool)) }, { 0 , Profile::Property(0) , 0 } }; 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())}, {0, 0, 0} }; 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)) }, { 0 , Profile::Property(0) , 0 } }; setupCheckBoxes(options , profile); // Setup the URL hints modifier checkboxes { int modifiers = profile->property(Profile::UrlHintsModifiers); _ui->urlHintsModifierShift->setChecked(modifiers & Qt::ShiftModifier); _ui->urlHintsModifierCtrl->setChecked(modifiers & Qt::ControlModifier); _ui->urlHintsModifierAlt->setChecked(modifiers & Qt::AltModifier); _ui->urlHintsModifierMeta->setChecked(modifiers & Qt::MetaModifier); 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 KCodecAction* 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(codec->name()); updateTempProfileProperty(Profile::DefaultEncoding, name); _ui->characterEncodingLabel->setText(codec->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::toggleCopyTextToClipboard(bool enable) { updateTempProfileProperty(Profile::AutoCopySelectedText, 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 All Font")); + 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) return; painter->setRenderHint(QPainter::Antialiasing); // Draw background QStyle *style = option.widget ? option.widget->style() : QApplication::style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, option.widget); // Draw name QPalette::ColorRole textColor = (option.state & QStyle::State_Selected) ? 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; qreal colorWidth = (qreal)width / TABLE_COLORS; int margin = 5; qreal heightForWidth = (colorWidth * 2) + option.fontMetrics.height() + margin; // temporary return QSize(width, static_cast(heightForWidth)); }