You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

476 lines
15 KiB

/*
Copyright 2007-2008 by Robert Knight <robertknight@gmail.com>
Copyright 2018 by Harald Sitter <sitter@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.
*/
#ifndef EDITPROFILEDIALOG_H
#define EDITPROFILEDIALOG_H
// Qt
#include <QAbstractItemDelegate>
#include <QHash>
#include <QButtonGroup>
#include <QWidget>
#include <QGridLayout>
// KDE
#include <KPageDialog>
#include <KNS3/Entry>
// Konsole
#include "Profile.h"
#include "Enumeration.h"
#include "ColorScheme.h"
#include "ColorSchemeEditor.h"
#include "KeyboardTranslatorManager.h"
#include "FontDialog.h"
class KPluralHandlingSpinBox;
class KLocalizedString;
class QItemSelectionModel;
namespace Ui {
class EditProfileGeneralPage;
class EditProfileTabsPage;
class EditProfileAppearancePage;
class EditProfileScrollingPage;
class EditProfileKeyboardPage;
class EditProfileMousePage;
class EditProfileAdvancedPage;
}
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 KPageDialog
{
Q_OBJECT
public:
/** Constructs a new dialog with the specified parent. */
explicit EditProfileDialog(QWidget *parent = nullptr);
~EditProfileDialog() 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(const 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() override;
// reimplemented
void reject() override;
void apply();
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
private Q_SLOTS:
QSize sizeHint() const override;
// sets up the specified tab page if necessary
void preparePage(KPageWidgetItem *current, KPageWidgetItem *before = nullptr);
// 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);
// tab page
void tabTitleFormatChanged(const QString &format);
void remoteTabTitleFormatChanged(const QString &format);
void tabColorChanged(const QColor &color);
void silenceSecondsChanged(int);
void terminalColumnsEntryChanged(int);
void terminalRowsEntryChanged(int);
void showTerminalSizeHint(bool);
void setDimWhenInactive(bool);
void showEnvironmentEditor();
// appearance page
void setAntialiasText(bool enable);
void setBoldIntense(bool enable);
void useFontLineCharacters(bool enable);
void newColorScheme();
void editColorScheme();
void saveColorScheme(const ColorScheme &scheme, bool isNewScheme);
void removeColorScheme();
void gotNewColorSchemes(const KNS3::Entry::List &changedEntries);
void toggleBlinkingCursor(bool);
void setCursorShape(int);
void autoCursorColor();
void customCursorColor();
void customCursorColorChanged(const QColor &);
void customCursorTextColorChanged(const QColor &);
void terminalMarginChanged(int margin);
void lineSpacingChanged(int);
void setTerminalCenter(bool enable);
/**
* 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 showFontDialog();
void toggleMouseWheelZoom(bool enable);
// scrolling page
void historyModeChanged(Enum::HistoryModeEnum mode);
void historySizeChanged(int);
void scrollFullPage();
void scrollHalfPage();
void toggleHighlightScrolledLines(bool enable);
// 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 updateUrlHintsModifier(bool);
void toggleReverseUrlHints(bool);
void setDefaultCodec(QTextCodec *);
// apply the first previewed changes stored up by delayedPreview()
void delayedPreviewActivate();
private:
Q_DISABLE_COPY(EditProfileDialog)
enum PageID {
GeneralPage = 0,
TabsPage,
AppearancePage,
ScrollingPage,
KeyboardPage,
MousePage,
AdvancedPage,
PagesCount
};
// 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 &profile);
int maxSpinBoxWidth(const KPluralHandlingSpinBox *spinBox, const KLocalizedString &suffix);
// 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();
// Convenience method
KeyboardTranslatorManager *_keyManager = KeyboardTranslatorManager::instance();
// Updates the key bindings list widget on the Keyboard tab and selects
// @p selectKeyBindingsName
void updateKeyBindingsList(const QString &selectKeyBindingsName = QString());
void updateKeyBindingsButtons();
void showKeyBindingEditor(bool isNewTranslator);
void showColorSchemeEditor(bool isNewScheme);
void closeColorSchemeEditor();
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();
void updateFontPreview(QFont font);
// 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(const QVector<RadioOption>& possibilities, int actual);
struct BooleanOption {
QAbstractButton *button;
Profile::Property property;
const char *slot;
};
void setupCheckBoxes(const QVector<BooleanOption>& options, const Profile::Ptr &profile);
struct ButtonGroupOption {
QAbstractButton *button;
int value;
};
struct ButtonGroupOptions {
QButtonGroup *group;
Profile::Property profileProperty;
bool preview;
QVector<ButtonGroupOption> buttons;
};
void setupButtonGroup(const ButtonGroupOptions &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::EditProfileGeneralPage *_generalUi;
Ui::EditProfileTabsPage *_tabsUi;
Ui::EditProfileAppearancePage *_appearanceUi;
Ui::EditProfileScrollingPage *_scrollingUi;
Ui::EditProfileKeyboardPage *_keyboardUi;
Ui::EditProfileMousePage *_mouseUi;
Ui::EditProfileAdvancedPage *_advancedUi;
using PageSetupMethod = void (EditProfileDialog::*)(const Profile::Ptr &);
struct Page {
Page(PageSetupMethod page = nullptr, bool update = false)
: setupPage(page)
, needsUpdate(update)
{}
PageSetupMethod setupPage;
bool needsUpdate;
};
QMap<KPageWidgetItem *, Page> _pages;
Profile::Ptr _tempProfile;
Profile::Ptr _profile;
QHash<int, QVariant> _previewedProperties;
QHash<int, QVariant> _delayedPreviewProperties;
QTimer *_delayedPreviewTimer;
ColorSchemeEditor *_colorDialog;
QDialogButtonBox *_buttonBox;
FontDialog *_fontDialog;
};
/**
* 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 override;
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
};
/**
* An utility class for aligning 0th column in multiple QGridLayouts.
*
* Limitations:
* - a layout can't be nested in another layout
* - reference widget must be an ancestor of all added layouts
* - only 0th column is processed (widgets spanning multiple columns
* are ignored)
*/
class LabelsAligner: public QObject {
Q_OBJECT
public:
explicit LabelsAligner(QWidget *refWidget): _refWidget(refWidget) {}
void addLayout(QGridLayout *layout) { _layouts.append(layout); }
void addLayouts(const QVector<QGridLayout *> &layouts) { _layouts.append(layouts); }
void setReferenceWidget(QWidget *refWidget) { _refWidget = refWidget; }
public Q_SLOTS:
void updateLayouts() {
for (const auto *layout: qAsConst(_layouts)) {
QWidget *widget = layout->parentWidget();
Q_ASSERT(widget);
do {
QLayout *widgetLayout = widget->layout();
if (widgetLayout != nullptr) {
widgetLayout->update();
widgetLayout->activate();
}
widget = widget->parentWidget();
} while (widget != _refWidget && widget != nullptr);
}
}
void align() {
Q_ASSERT(_refWidget);
if (_layouts.count() <= 1) {
return;
}
int maxRight = 0;
for (const auto *layout: qAsConst(_layouts)) {
int left = getLeftMargin(layout);
for (int row = 0; row < layout->rowCount(); ++row) {
QLayoutItem *layoutItem = layout->itemAtPosition(row, LABELS_COLUMN);
if (layoutItem == nullptr) {
continue;
}
QWidget *widget = layoutItem->widget();
if (widget == nullptr) {
continue;
}
const int idx = layout->indexOf(widget);
int rows, cols, rowSpan, colSpan;
layout->getItemPosition(idx, &rows, &cols, &rowSpan, &colSpan);
if (colSpan > 1) {
continue;
}
const int right = left + widget->sizeHint().width();
if (maxRight < right) {
maxRight = right;
}
}
}
for (auto *l: qAsConst(_layouts)) {
int left = getLeftMargin(l);
l->setColumnMinimumWidth(LABELS_COLUMN, maxRight - left);
}
}
private:
int getLeftMargin(const QGridLayout *layout) {
int left = layout->contentsMargins().left();
if (layout->parent()->isWidgetType()) {
auto *parentWidget = layout->parentWidget();
Q_ASSERT(parentWidget);
left += parentWidget->contentsMargins().left();
} else {
auto *parentLayout = qobject_cast<QLayout *>(layout->parent());
Q_ASSERT(parentLayout);
left += parentLayout->contentsMargins().left();
}
QWidget *parent = layout->parentWidget();
while (parent != _refWidget && parent != nullptr) {
left = parent->mapToParent(QPoint(left, 0)).x();
parent = parent->parentWidget();
}
return left;
}
static constexpr int LABELS_COLUMN = 0;
QWidget *_refWidget;
QVector<QGridLayout *> _layouts;
};
}
#endif // EDITPROFILEDIALOG_H