Files
konsole/src/EditProfileDialog.cpp
Jekyll Wu 2f7a0965f5 Move class ColorSchemeMananger into its own file
class KDE3ColorSchemeReader is also moved since it is supposed to an
compatibility helpr of which class ColorScheme does not need to know,
and eventualy it should be removed
2012-02-29 22:31:56 +08:00

1353 lines
47 KiB
C++

/*
Copyright 2007-2008 by Robert Knight <robertknight@gmail.com>
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 <cmath>
// Qt
#include <QtGui/QBrush>
#include <QtGui/QPainter>
#include <QtGui/QStandardItem>
#include <QtCore/QTextCodec>
#include <QtGui/QLinearGradient>
#include <QtGui/QRadialGradient>
#include <QtCore/QTimer>
#include <QtCore/QTimeLine>
// KDE
#include <kcodecaction.h>
#include <KFontDialog>
#include <KIcon>
#include <KIconDialog>
#include <KFileDialog>
#include <KUrlCompletion>
#include <KWindowSystem>
#include <KTextEdit>
#include <KMessageBox>
// Konsole
#include "ColorScheme.h"
#include "ColorSchemeManager.h"
#include "ColorSchemeEditor.h"
#include "ui_EditProfileDialog.h"
#include "KeyBindingEditor.h"
#include "KeyboardTranslator.h"
#include "SessionManager.h"
#include "ShellCommand.h"
#include "TabTitleFormatAction.h"
#include "WindowSystemInfo.h"
using namespace Konsole;
EditProfileDialog::EditProfileDialog(QWidget* parent)
: KDialog(parent)
, _colorSchemeAnimationTimeLine(0)
, _delayedPreviewTimer(new QTimer(this))
{
setCaption(i18n("Edit Profile"));
setButtons(KDialog::Ok | KDialog::Cancel | KDialog::Apply);
// disable the apply button , since no modification has been made
enableButtonApply(false);
connect(this, SIGNAL(applyClicked()), this, SLOT(save()));
connect(_delayedPreviewTimer, SIGNAL(timeout()), this, SLOT(delayedPreviewActivate()));
_ui = new Ui::EditProfileDialog();
_ui->setupUi(mainWidget());
// TODO: Re-enable in a later KDE 4.x release when this feature works again
_ui->enableResizeWindowButton->setVisible(false);
// 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, SIGNAL(currentChanged(int)), this,
SLOT(preparePage(int)));
createTempProfile();
}
EditProfileDialog::~EditProfileDialog()
{
delete _ui;
}
void EditProfileDialog::save()
{
if (_tempProfile->isEmpty())
return;
SessionManager::instance()->changeProfile(_profile, _tempProfile->setProperties());
// ensure that these settings are not undone by a call
// to unpreview()
QHashIterator<Profile::Property, QVariant> iter(_tempProfile->setProperties());
while (iter.hasNext()) {
iter.next();
_previewedProperties.remove(iter.key());
}
createTempProfile();
enableButtonApply(false);
}
void EditProfileDialog::reject()
{
unpreviewAll();
KDialog::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("<p>Each profile must have a name before it can be saved "
"into disk.</p>"));
return;
}
save();
unpreviewAll();
KDialog::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 += "...";
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);
setCaption(i18np("Editing profile: %2",
"Editing %1 profiles: %2",
group->profiles().count(),
caption));
} else {
setCaption(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->advancedTab)
setupAdvancedPage(profile);
else
Q_ASSERT(false);
_pageNeedsUpdate[page] = false;
}
// start page entry animation for color schemes
if (pageWidget == _ui->appearanceTab)
_colorSchemeAnimationTimeLine->start();
}
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->setClearButtonShown(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());
KUrlCompletion* exeCompletion = new KUrlCompletion(KUrlCompletion::ExeCompletion);
exeCompletion->setParent(this);
exeCompletion->setDir(QString());
_ui->commandEdit->setCompletionObject(exeCompletion);
_ui->initialDirEdit->setText(profile->defaultWorkingDirectory());
KUrlCompletion* dirCompletion = new KUrlCompletion(KUrlCompletion::DirCompletion);
dirCompletion->setParent(this);
_ui->initialDirEdit->setCompletionObject(dirCompletion);
_ui->initialDirEdit->setClearButtonShown(true);
_ui->dirSelectButton->setIcon(KIcon("folder-open"));
_ui->iconSelectButton->setIcon(KIcon(profile->icon()));
_ui->startInSameDirButton->setChecked(profile->property<bool>(Profile::StartInCurrentSessionDir));
// window options
_ui->showTerminalSizeHintButton->setChecked(profile->property<bool>(Profile::ShowTerminalSizeHint));
_ui->saveGeometryOnExitButton->setChecked(profile->property<bool>(Profile::SaveGeometryOnExit));
// signals and slots
connect(_ui->dirSelectButton, SIGNAL(clicked()), this, SLOT(selectInitialDir()));
connect(_ui->iconSelectButton, SIGNAL(clicked()), this, SLOT(selectIcon()));
connect(_ui->startInSameDirButton, SIGNAL(toggled(bool)), this ,
SLOT(startInSameDir(bool)));
connect(_ui->profileNameEdit, SIGNAL(textChanged(QString)), this,
SLOT(profileNameChanged(QString)));
connect(_ui->initialDirEdit, SIGNAL(textChanged(QString)), this,
SLOT(initialDirChanged(QString)));
connect(_ui->commandEdit, SIGNAL(textChanged(QString)), this,
SLOT(commandChanged(QString)));
connect(_ui->environmentEditButton , SIGNAL(clicked()), this,
SLOT(showEnvironmentEditor()));
connect(_ui->saveGeometryOnExitButton, SIGNAL(toggled(bool)), this,
SLOT(saveGeometryOnExit(bool)));
connect(_ui->showTerminalSizeHintButton, SIGNAL(toggled(bool)), this,
SLOT(showTerminalSizeHint(bool)));
}
void EditProfileDialog::showEnvironmentEditor()
{
const Profile::Ptr profile = lookupProfile();
QWeakPointer<KDialog> dialog = new KDialog(this);
KTextEdit* edit = new KTextEdit(dialog.data());
QStringList currentEnvironment = profile->property<QStringList>(Profile::Environment);
edit->setPlainText(currentEnvironment.join("\n"));
edit->setToolTip(i18n("One environment variable per line"));
dialog.data()->setPlainCaption(i18n("Edit Environment"));
dialog.data()->setMainWidget(edit);
if (dialog.data()->exec() == QDialog::Accepted) {
QStringList newEnvironment = edit->toPlainText().split('\n');
updateTempProfileProperty(Profile::Environment, newEnvironment);
}
delete dialog.data();
}
void EditProfileDialog::setupTabsPage(const Profile::Ptr profile)
{
// tab title format
_ui->tabTitleEdit->setClearButtonShown(true);
_ui->remoteTabTitleEdit->setClearButtonShown(true);
_ui->tabTitleEdit->setText(profile->property<QString>(Profile::LocalTabTitleFormat));
_ui->remoteTabTitleEdit->setText(
profile->property<QString>(Profile::RemoteTabTitleFormat));
// tab monitoring
int silenceSeconds = profile->property<int>(Profile::SilenceSeconds);
_ui->silenceSecondsSpinner->setValue(silenceSeconds);
_ui->silenceSecondsSpinner->setSuffix(ki18ncp("Unit of time", " second", " seconds"));
connect(_ui->tabTitleEdit, SIGNAL(textChanged(QString)), this,
SLOT(tabTitleFormatChanged(QString)));
connect(_ui->remoteTabTitleEdit, SIGNAL(textChanged(QString)), this,
SLOT(remoteTabTitleFormatChanged(QString)));
connect(_ui->silenceSecondsSpinner, SIGNAL(valueChanged(int)),
this, SLOT(silenceSecondsChanged(int)));
// menus for local and remote tab title dynamic elements
TabTitleFormatAction* localTabTitleAction = new TabTitleFormatAction(this);
localTabTitleAction->setContext(Session::LocalTabTitle);
_ui->tabTitleEditButton->setMenu(localTabTitleAction->menu());
connect(localTabTitleAction, SIGNAL(dynamicElementSelected(QString)),
this, SLOT(insertTabTitleText(QString)));
TabTitleFormatAction* remoteTabTitleAction = new TabTitleFormatAction(this);
remoteTabTitleAction->setContext(Session::RemoteTabTitle);
_ui->remoteTabTitleEditButton->setMenu(remoteTabTitleAction->menu());
connect(remoteTabTitleAction, SIGNAL(dynamicElementSelected(QString)),
this, SLOT(insertRemoteTabTitleText(QString)));
}
void EditProfileDialog::insertTabTitleText(const QString& text)
{
_ui->tabTitleEdit->insert(text);
}
void EditProfileDialog::insertRemoteTabTitleText(const QString& text)
{
_ui->remoteTabTitleEdit->insert(text);
}
void EditProfileDialog::saveGeometryOnExit(bool save)
{
updateTempProfileProperty(Profile::SaveGeometryOnExit, save);
}
void EditProfileDialog::showTerminalSizeHint(bool show)
{
updateTempProfileProperty(Profile::ShowTerminalSizeHint, show);
}
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(KIcon(icon));
updateTempProfileProperty(Profile::Icon, icon);
}
}
void EditProfileDialog::profileNameChanged(const QString& text)
{
_ui->emptyNameWarningWidget->setVisible(text.isEmpty());
updateTempProfileProperty(Profile::Name, 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 KUrl url = KFileDialog::getExistingDirectoryUrl(_ui->initialDirEdit->text(),
this,
i18n("Select Initial Directory"));
if (!url.isEmpty())
_ui->initialDirEdit->setText(url.path());
}
void EditProfileDialog::setupAppearancePage(const Profile::Ptr profile)
{
ColorSchemeViewDelegate* delegate = new ColorSchemeViewDelegate(this);
_ui->colorSchemeList->setItemDelegate(delegate);
_colorSchemeAnimationTimeLine = new QTimeLine(500 , this);
delegate->setEntryTimeLine(_colorSchemeAnimationTimeLine);
connect(_colorSchemeAnimationTimeLine, SIGNAL(valueChanged(qreal)), this,
SLOT(colorSchemeAnimationUpdate()));
_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(),
SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(colorSchemeSelected()));
connect(_ui->colorSchemeList, SIGNAL(entered(QModelIndex)), this,
SLOT(previewColorScheme(QModelIndex)));
updateColorSchemeButtons();
connect(_ui->editColorSchemeButton, SIGNAL(clicked()), this,
SLOT(editColorScheme()));
connect(_ui->removeColorSchemeButton, SIGNAL(clicked()), this,
SLOT(removeColorScheme()));
connect(_ui->newColorSchemeButton, SIGNAL(clicked()), this,
SLOT(newColorScheme()));
// setup font preview
bool antialias = profile->property<bool>(Profile::AntiAliasFonts);
QFont font = profile->font();
font.setStyleStrategy(antialias ? QFont::PreferAntialias : QFont::NoAntialias);
_ui->fontPreviewLabel->installEventFilter(this);
_ui->fontPreviewLabel->setFont(font);
setFontSliderRange(font);
setFontSliderValue(font);
connect(_ui->fontSizeSlider, SIGNAL(valueChanged(int)), this,
SLOT(setFontSize(int)));
connect(_ui->editFontButton, SIGNAL(clicked()), this,
SLOT(showFontDialog()));
// setup font smoothing
_ui->antialiasTextButton->setChecked(antialias);
connect(_ui->antialiasTextButton, SIGNAL(toggled(bool)), this,
SLOT(setAntialiasText(bool)));
bool boldIntense = profile->property<bool>(Profile::BoldIntense);
_ui->boldIntenseButton->setChecked(boldIntense);
connect(_ui->boldIntenseButton, SIGNAL(toggled(bool)), this,
SLOT(setBoldIntense(bool)));
}
void EditProfileDialog::setAntialiasText(bool enable)
{
QFont font = _ui->fontPreviewLabel->font();
font.setStyleStrategy(enable ? QFont::PreferAntialias : QFont::NoAntialias);
// update preview to reflect text smoothing state
fontSelected(font);
updateTempProfileProperty(Profile::AntiAliasFonts, enable);
}
void EditProfileDialog::setBoldIntense(bool enable)
{
preview(Profile::BoldIntense, enable);
updateTempProfileProperty(Profile::BoldIntense, enable);
}
void EditProfileDialog::colorSchemeAnimationUpdate()
{
QAbstractItemModel* model = _ui->colorSchemeList->model();
for (int i = model->rowCount() ; i >= 0 ; i--)
_ui->colorSchemeList->update(model->index(i, 0));
}
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<QStandardItemModel*>(_ui->colorSchemeList->model());
Q_ASSERT(model);
model->clear();
QStandardItem* selectedItem = 0;
QList<const ColorScheme*> schemeList = ColorSchemeManager::instance()->allColorSchemes();
foreach ( const ColorScheme* scheme, schemeList) {
QStandardItem* item = new QStandardItem(scheme->description());
item->setData(QVariant::fromValue(scheme) , Qt::UserRole + 1);
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()
->property<QString>(Profile::KeyBindings);
KeyboardTranslatorManager* keyManager = KeyboardTranslatorManager::instance();
const KeyboardTranslator* currentTranslator = keyManager->findTranslator(name);
QStandardItemModel* model = qobject_cast<QStandardItemModel*>(_ui->keyBindingList->model());
Q_ASSERT(model);
model->clear();
QStandardItem* selectedItem = 0;
QList<QString> translatorNames = keyManager->allTranslators();
foreach ( const QString& name, translatorNames) {
const KeyboardTranslator* translator = keyManager->findTranslator(name);
QStandardItem* item = new QStandardItem(translator->description());
item->setData(QVariant::fromValue(translator), Qt::UserRole + 1);
item->setIcon(KIcon("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* 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();
qreal size = labelFont.pointSizeF();
QString fontSize = KGlobal::locale()->formatNumber(size, size == floor(size) ? 0 : 1);
_ui->fontPreviewLabel->setText(i18n("%1, size %2", labelFont.family(), fontSize));
}
return KDialog::eventFilter(watched, event);
}
void EditProfileDialog::unpreviewAll()
{
_delayedPreviewTimer->stop();
_delayedPreviewProperties.clear();
QHash<Profile::Property, QVariant> map;
QHashIterator<int, QVariant> iter(_previewedProperties);
while (iter.hasNext()) {
iter.next();
map.insert((Profile::Property)iter.key(), iter.value());
}
// undo any preview changes
if (!map.isEmpty())
SessionManager::instance()->changeProfile(_profile, map, false);
}
void EditProfileDialog::unpreview(int property)
{
_delayedPreviewProperties.remove(property);
if (!_previewedProperties.contains(property))
return;
QHash<Profile::Property, QVariant> map;
map.insert((Profile::Property)property, _previewedProperties[property]);
SessionManager::instance()->changeProfile(_profile, map, false);
_previewedProperties.remove(property);
}
void EditProfileDialog::delayedPreview(int property , const QVariant& value)
{
_delayedPreviewProperties.insert(property, value);
_delayedPreviewTimer->stop();
_delayedPreviewTimer->start(300);
}
void EditProfileDialog::delayedPreviewActivate()
{
Q_ASSERT(qobject_cast<QTimer*>(sender()));
QMutableHashIterator<int, QVariant> iter(_delayedPreviewProperties);
if (iter.hasNext()) {
iter.next();
preview(iter.key(), iter.value());
}
}
void EditProfileDialog::preview(int property , const QVariant& value)
{
QHash<Profile::Property, QVariant> map;
map.insert((Profile::Property)property, value);
_delayedPreviewProperties.remove(property);
const Profile::Ptr original = lookupProfile();
// skip previews for profile groups if the profiles in the group
// have conflicting original values for the property
//
// TODO - Save the original values for each profile and use to unpreview properties
ProfileGroup::Ptr group = original->asGroup();
if (group && group->profiles().count() > 1 &&
original->property<QVariant>((Profile::Property)property).isNull())
return;
if (!_previewedProperties.contains(property)) {
_previewedProperties.insert(property , original->property<QVariant>((Profile::Property)property));
}
// temporary change to color scheme
SessionManager::instance()->changeProfile(_profile , map , false);
}
void EditProfileDialog::previewColorScheme(const QModelIndex& index)
{
const QString& name = index.data(Qt::UserRole + 1).value<const ColorScheme*>()->name();
delayedPreview(Profile::ColorScheme , name);
}
void EditProfileDialog::removeColorScheme()
{
QModelIndexList selected = _ui->colorSchemeList->selectionModel()->selectedIndexes();
if (!selected.isEmpty()) {
const QString& name = selected.first().data(Qt::UserRole + 1).value<const ColorScheme*>()->name();
if (ColorSchemeManager::instance()->deleteColorScheme(name))
_ui->colorSchemeList->model()->removeRow(selected.first().row());
}
}
void EditProfileDialog::showColorSchemeEditor(bool isNewScheme)
{
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<const ColorScheme*>();
else
colors = ColorSchemeManager::instance()->defaultColorScheme();
Q_ASSERT(colors);
QWeakPointer<KDialog> dialog = new KDialog(this);
if (isNewScheme)
dialog.data()->setCaption(i18n("New Color Scheme"));
else
dialog.data()->setCaption(i18n("Edit Color Scheme"));
ColorSchemeEditor* editor = new ColorSchemeEditor;
dialog.data()->setMainWidget(editor);
editor->setup(colors);
if (isNewScheme)
editor->setDescription(i18n("New Color Scheme"));
if (dialog.data()->exec() == QDialog::Accepted) {
ColorScheme* newScheme = new ColorScheme(*editor->colorScheme());
// 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());
}
delete dialog.data();
}
void EditProfileDialog::newColorScheme()
{
showColorSchemeEditor(true);
}
void EditProfileDialog::editColorScheme()
{
showColorSchemeEditor(false);
}
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<const ColorScheme*>();
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<const ColorScheme*>()->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<Profile::Property, QVariant> iter(_tempProfile->setProperties());
while (iter.hasNext()) {
iter.next();
Profile::Property property = iter.key();
QVariant value = iter.value() ;
// for previewed property
if (_previewedProperties.contains(int(property))) {
if (value != _previewedProperties.value(int(property))) {
userModified = true ;
break ;
}
}
// for not-previewed property
else if ((value != _profile->property<QVariant>(property))) {
userModified = true ;
break ;
}
}
enableButtonApply(userModified);
}
void EditProfileDialog::setupKeyboardPage(const Profile::Ptr /* profile */)
{
// setup translator list
updateKeyBindingsList(true);
connect(_ui->keyBindingList->selectionModel(),
SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
SLOT(keyBindingSelected()));
connect(_ui->newKeyBindingsButton, SIGNAL(clicked()), this,
SLOT(newKeyBinding()));
updateKeyBindingsButtons();
connect(_ui->editKeyBindingsButton, SIGNAL(clicked()), this,
SLOT(editKeyBinding()));
connect(_ui->removeKeyBindingsButton, SIGNAL(clicked()), this,
SLOT(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<const KeyboardTranslator*>();
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<const KeyboardTranslator*>()->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<const KeyboardTranslator*>();
else
translator = KeyboardTranslatorManager::instance()->defaultTranslator();
Q_ASSERT(translator);
QWeakPointer<KDialog> dialog = new KDialog(this);
if (isNewTranslator)
dialog.data()->setCaption(i18n("New Key Binding List"));
else
dialog.data()->setCaption(i18n("Edit Key Binding List"));
KeyBindingEditor* editor = new KeyBindingEditor;
dialog.data()->setMainWidget(editor);
if (translator)
editor->setup(translator);
if (isNewTranslator)
editor->setDescription(i18n("New Key Binding List"));
if (dialog.data()->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<QString>(Profile::KeyBindings);
if (newTranslator->name() == currentTranslator) {
updateTempProfileProperty(Profile::KeyBindings, newTranslator->name());
}
}
delete dialog.data();
}
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<bool>(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<int>(Profile::ScrollBarPosition);
RadioOption positions[] = { {_ui->scrollBarHiddenButton, Profile::ScrollBarHidden, SLOT(hideScrollBar())},
{_ui->scrollBarLeftButton, Profile::ScrollBarLeft, SLOT(showScrollBarLeft())},
{_ui->scrollBarRightButton, Profile::ScrollBarRight, SLOT(showScrollBarRight())},
{0, 0, 0}
};
setupRadio(positions , scrollBarPosition);
// setup scrollback type radio
int scrollBackType = profile->property<int>(Profile::HistoryMode);
RadioOption types[] = { {_ui->disableScrollbackButton, Profile::DisableHistory, SLOT(noScrollBack())},
{_ui->fixedScrollbackButton, Profile::FixedSizeHistory, SLOT(fixedScrollBack())},
{_ui->unlimitedScrollbackButton, Profile::UnlimitedHistory, SLOT(unlimitedScrollBack())},
{0, 0, 0}
};
setupRadio(types , scrollBackType);
// setup scrollback line count spinner
int historySize = profile->property<int>(Profile::HistorySize);
_ui->scrollBackLinesSpinner->setValue(historySize);
_ui->scrollBackLinesSpinner->setSingleStep(historySize / 10);
_ui->scrollBackLinesSpinner->setSuffix(ki18ncp("Unit of scrollback", " line", " lines"));
// signals and slots
connect(_ui->scrollBackLinesSpinner, SIGNAL(valueChanged(int)), this,
SLOT(scrollBackLinesChanged(int)));
}
void EditProfileDialog::scrollBackLinesChanged(int lineCount)
{
updateTempProfileProperty(Profile::HistorySize , lineCount);
}
void EditProfileDialog::noScrollBack()
{
updateTempProfileProperty(Profile::HistoryMode , Profile::DisableHistory);
}
void EditProfileDialog::fixedScrollBack()
{
updateTempProfileProperty(Profile::HistoryMode , Profile::FixedSizeHistory);
}
void EditProfileDialog::unlimitedScrollBack()
{
updateTempProfileProperty(Profile::HistoryMode , Profile::UnlimitedHistory);
}
void EditProfileDialog::hideScrollBar()
{
updateTempProfileProperty(Profile::ScrollBarPosition , Profile::ScrollBarHidden);
}
void EditProfileDialog::showScrollBarLeft()
{
updateTempProfileProperty(Profile::ScrollBarPosition , Profile::ScrollBarLeft);
}
void EditProfileDialog::showScrollBarRight()
{
updateTempProfileProperty(Profile::ScrollBarPosition , Profile::ScrollBarRight);
}
void EditProfileDialog::setupAdvancedPage(const Profile::Ptr profile)
{
BooleanOption options[] = { {
_ui->enableBlinkingTextButton , Profile::BlinkingTextEnabled ,
SLOT(toggleBlinkingText(bool))
},
{
_ui->enableFlowControlButton , Profile::FlowControlEnabled ,
SLOT(toggleFlowControl(bool))
},
{
_ui->enableResizeWindowButton , Profile::AllowProgramsToResizeWindow ,
SLOT(toggleResizeWindow(bool))
},
{
_ui->enableBlinkingCursorButton , Profile::BlinkingCursorEnabled ,
SLOT(toggleBlinkingCursor(bool))
},
{
_ui->underlineLinksButton , Profile::UnderlineLinksEnabled,
SLOT(toggleUnderlineLinks(bool))
},
{
_ui->enableBidiRenderingButton , Profile::BidiRenderingEnabled ,
SLOT(togglebidiRendering(bool))
},
{ 0 , Profile::Property(0) , 0 }
};
setupCheckBoxes(options , profile);
// interaction options
_ui->wordCharacterEdit->setText(profile->property<QString>(Profile::WordCharacters));
connect(_ui->wordCharacterEdit, SIGNAL(textChanged(QString)), this,
SLOT(wordCharactersChanged(QString)));
int tripleClickMode = profile->property<int>(Profile::TripleClickMode);
_ui->tripleClickModeCombo->setCurrentIndex(tripleClickMode);
connect(_ui->tripleClickModeCombo, SIGNAL(activated(int)), this,
SLOT(TripleClickModeChanged(int)));
// cursor options
if (profile->property<bool>(Profile::UseCustomCursorColor))
_ui->customCursorColorButton->setChecked(true);
else
_ui->autoCursorColorButton->setChecked(true);
_ui->customColorSelectButton->setColor(profile->property<QColor>(Profile::CustomCursorColor));
connect(_ui->customCursorColorButton, SIGNAL(clicked()), this, SLOT(customCursorColor()));
connect(_ui->autoCursorColorButton, SIGNAL(clicked()), this, SLOT(autoCursorColor()));
connect(_ui->customColorSelectButton, SIGNAL(changed(QColor)),
SLOT(customCursorColorChanged(QColor)));
int shape = profile->property<int>(Profile::CursorShape);
_ui->cursorShapeCombo->setCurrentIndex(shape);
connect(_ui->cursorShapeCombo, SIGNAL(activated(int)), this, SLOT(setCursorShape(int)));
// encoding options
QAction* codecAction = new KCodecAction(this);
_ui->selectEncodingButton->setMenu(codecAction->menu());
connect(codecAction, SIGNAL(triggered(QTextCodec*)), this, SLOT(setDefaultCodec(QTextCodec*)));
_ui->characterEncodingLabel->setText(profile->property<QString>(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::toggleBlinkingCursor(bool enable)
{
updateTempProfileProperty(Profile::BlinkingCursorEnabled, enable);
}
void EditProfileDialog::toggleUnderlineLinks(bool enable)
{
updateTempProfileProperty(Profile::UnderlineLinksEnabled, enable);
}
void EditProfileDialog::TripleClickModeChanged(int newValue)
{
updateTempProfileProperty(Profile::TripleClickMode, newValue);
}
void EditProfileDialog::toggleBlinkingText(bool enable)
{
updateTempProfileProperty(Profile::BlinkingTextEnabled, enable);
}
void EditProfileDialog::toggleFlowControl(bool enable)
{
updateTempProfileProperty(Profile::FlowControlEnabled, enable);
}
void EditProfileDialog::toggleResizeWindow(bool enable)
{
updateTempProfileProperty(Profile::AllowProgramsToResizeWindow, enable);
}
void EditProfileDialog::fontSelected(const QFont& font)
{
QFont previewFont = font;
setFontSliderRange(font);
setFontSliderValue(font);
_ui->fontPreviewLabel->setFont(previewFont);
preview(Profile::Font, font);
updateTempProfileProperty(Profile::Font, font);
}
void EditProfileDialog::showFontDialog()
{
QString sampleText = QString("ell 'lL', one '1', little eye 'i', big eye");
sampleText += QString("'I', lL1iI, Zero '0', little oh 'o', big oh 'O', 0oO");
sampleText += QString("`~!@#$%^&*()_+-=[]\{}|:\";'<>?,./");
sampleText += QString("0123456789");
sampleText += QString("\nThe Quick Brown Fox Jumps Over The Lazy Dog\n");
sampleText += i18n("--- Type anything in this box ---");
QFont currentFont = _ui->fontPreviewLabel->font();
QWeakPointer<KFontDialog> dialog = new KFontDialog(this, KFontChooser::FixedFontsOnly);
dialog.data()->setFont(currentFont, true);
// TODO (hindenburg): When https://git.reviewboard.kde.org/r/103357 is
// committed, change the below.
// Use text more fitting to show font differences in a terminal
QList<KFontChooser*> chooserList = dialog.data()->findChildren<KFontChooser*>();
if (!chooserList.isEmpty())
chooserList.at(0)->setSampleText(sampleText);
connect(dialog.data(), SIGNAL(fontSelected(QFont)), this, SLOT(fontSelected(QFont)));
if (dialog.data()->exec() == QDialog::Rejected)
fontSelected(currentFont);
delete dialog.data();
}
void EditProfileDialog::setFontSize(int pointSize)
{
QFont newFont = _ui->fontPreviewLabel->font();
newFont.setPointSizeF(pointSize / 10.0);
_ui->fontPreviewLabel->setFont(newFont);
preview(Profile::Font, newFont);
updateTempProfileProperty(Profile::Font, newFont);
}
void EditProfileDialog::setFontSliderRange(const QFont& font)
{
QSlider* slider = _ui->fontSizeSlider;
// Minimum on the slider is 4,
// Maximum is the greater of 2 times the current size and 14
slider->setRange(qMin(4 * 10, qRound(font.pointSizeF() * 10)),
qMax(14 * 10, 2 * qRound(font.pointSize() * 10)));
}
void EditProfileDialog::setFontSliderValue(const QFont& font)
{
_ui->fontSizeSlider->setValue(qRound(font.pointSize() * 10));
}
ColorSchemeViewDelegate::ColorSchemeViewDelegate(QObject* parent)
: QAbstractItemDelegate(parent)
{
}
void ColorSchemeViewDelegate::setEntryTimeLine(QTimeLine* timeLine)
{
_entryTimeLine = timeLine;
}
void ColorSchemeViewDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
// entry animation
//
// note that the translation occurs for each item drawn, but the
// painter is not reset between painting items. this means that when
// the items are painted in order ( as occurs when the list is first
// shown ), there is a visually pleasing staggering of items as they
// enter.
if (_entryTimeLine != 0) {
qreal value = 1.0 - _entryTimeLine->currentValue();
painter->translate(value *
option.rect.width() , 0);
painter->setOpacity(_entryTimeLine->currentValue());
}
const ColorScheme* scheme = index.data(Qt::UserRole + 1).value<const ColorScheme*>();
Q_ASSERT(scheme);
bool transparencyAvailable = KWindowSystem::compositingActive();
painter->setRenderHint(QPainter::Antialiasing);
// draw background
painter->setPen(QPen(scheme->foregroundColor() , 1));
// radial gradient for background
// from a lightened version of the scheme's background color in the center to
// a darker version at the outer edge
QColor color = scheme->backgroundColor();
QRectF backgroundRect = QRectF(option.rect).adjusted(1.5, 1.5, -1.5, -1.5);
QRadialGradient backgroundGradient(backgroundRect.center() , backgroundRect.width() / 2);
backgroundGradient.setColorAt(0 , color.lighter(105));
backgroundGradient.setColorAt(1 , color.darker(115));
const int backgroundRectXRoundness = 4;
const int backgroundRectYRoundness = 30;
QPainterPath backgroundRectPath(backgroundRect.topLeft());
backgroundRectPath.addRoundRect(backgroundRect , backgroundRectXRoundness , backgroundRectYRoundness);
if (transparencyAvailable) {
painter->save();
color.setAlphaF(scheme->opacity());
painter->setCompositionMode(QPainter::CompositionMode_Source);
painter->setBrush(backgroundGradient);
painter->drawPath(backgroundRectPath);
painter->restore();
} else {
painter->setBrush(backgroundGradient);
painter->drawPath(backgroundRectPath);
}
// draw stripe at the side using scheme's foreground color
painter->setPen(QPen(Qt::NoPen));
QPainterPath path(option.rect.topLeft());
path.lineTo(option.rect.width() / 10.0 , option.rect.top());
path.lineTo(option.rect.bottomLeft());
path.lineTo(option.rect.topLeft());
painter->setBrush(scheme->foregroundColor());
painter->drawPath(path.intersected(backgroundRectPath));
// draw highlight
// with a linear gradient going from translucent white to transparent
QLinearGradient gradient(option.rect.topLeft() , option.rect.bottomLeft());
gradient.setColorAt(0 , QColor(255, 255, 255, 90));
gradient.setColorAt(1 , Qt::transparent);
painter->setBrush(gradient);
painter->drawRoundRect(backgroundRect , 4 , 30);
//const bool isChecked = index.data(Qt::CheckStateRole) == Qt::Checked;
const bool isSelected = option.state & QStyle::State_Selected;
// draw border on selected items
if (isSelected) { //|| isChecked )
static const int selectedBorderWidth = 6;
painter->setBrush(QBrush(Qt::NoBrush));
QPen pen;
QColor highlightColor = option.palette.highlight().color();
if (isSelected)
highlightColor.setAlphaF(1.0);
else
highlightColor.setAlphaF(0.7);
pen.setBrush(highlightColor);
pen.setWidth(selectedBorderWidth);
pen.setJoinStyle(Qt::MiterJoin);
painter->setPen(pen);
painter->drawRect(option.rect.adjusted(selectedBorderWidth / 2,
selectedBorderWidth / 2,
-selectedBorderWidth / 2,
-selectedBorderWidth / 2));
}
// draw color scheme name using scheme's foreground color
QPen pen(scheme->foregroundColor());
painter->setPen(pen);
painter->drawText(option.rect , Qt::AlignCenter ,
index.data(Qt::DisplayRole).value<QString>());
}
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, (int)heightForWidth);
}
#include "EditProfileDialog.moc"