Commit 8aaf4318 authored by Tomaz  Canabrava's avatar Tomaz Canabrava Committed by Kurt Hindenburg
Browse files

New class: ProfileModel

The code that managed the profile model currently is done
inside of the ProfileSettings, and thus unusable out
of it. Since I want to make it easier to select a profile
this needs to split.

It's also a bad code style to mix interface and code
parent befddfcd
......@@ -248,6 +248,7 @@ set(konsole_KDEINIT_SRCS
settings/ProfileSettings.cpp
settings/TabBarSettings.cpp
settings/ThumbnailsSettings.cpp
delegates/ProfileShortcutDelegate.cpp
)
......@@ -281,7 +282,10 @@ install(TARGETS kdeinit_konsole konsole
set(konsolepart_PART_SRCS Part.cpp
settings/PartInfo.cpp
settings/ProfileSettings.cpp)
settings/ProfileSettings.cpp
delegates/ProfileShortcutDelegate.cpp
)
add_library(konsolepart MODULE ${konsolepart_PART_SRCS})
generate_export_header(konsolepart BASE_NAME konsole)
kcoreaddons_desktop_to_json(konsolepart ../desktop/konsolepart.desktop)
......
#include "ProfileShortcutDelegate.h"
#include <QApplication>
#include <QKeyEvent>
#include <QPainter>
using namespace Konsole;
ShortcutItemDelegate::ShortcutItemDelegate(QObject* parent)
: QStyledItemDelegate(parent),
_modifiedEditors(QSet<QWidget *>()),
_itemsBeingEdited(QSet<QModelIndex>())
{
}
void ShortcutItemDelegate::editorModified()
{
auto* editor = qobject_cast<FilteredKeySequenceEdit*>(sender());
Q_ASSERT(editor);
_modifiedEditors.insert(editor);
emit commitData(editor);
emit closeEditor(editor);
}
void ShortcutItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* model,
const QModelIndex& index) const
{
_itemsBeingEdited.remove(index);
if (!_modifiedEditors.contains(editor)) {
return;
}
QString shortcut = qobject_cast<FilteredKeySequenceEdit *>(editor)->keySequence().toString();
model->setData(index, shortcut, Qt::DisplayRole);
_modifiedEditors.remove(editor);
}
QWidget* ShortcutItemDelegate::createEditor(QWidget* aParent, const QStyleOptionViewItem&, const QModelIndex& index) const
{
_itemsBeingEdited.insert(index);
auto editor = new FilteredKeySequenceEdit(aParent);
QString shortcutString = index.data(Qt::DisplayRole).toString();
editor->setKeySequence(QKeySequence::fromString(shortcutString));
connect(editor, &QKeySequenceEdit::editingFinished, this, &Konsole::ShortcutItemDelegate::editorModified);
editor->setFocus(Qt::FocusReason::MouseFocusReason);
return editor;
}
void ShortcutItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
if (_itemsBeingEdited.contains(index)) {
StyledBackgroundPainter::drawBackground(painter, option, index);
} else {
QStyledItemDelegate::paint(painter, option, index);
}
}
QSize ShortcutItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
const QString shortcutString = index.data(Qt::DisplayRole).toString();
QFontMetrics fm = option.fontMetrics;
static const int editorMargins = 16; // chosen empirically
const int width = fm.boundingRect(shortcutString + QStringLiteral(", ...")).width()
+ editorMargins;
return {width, QStyledItemDelegate::sizeHint(option, index).height()};
}
void ShortcutItemDelegate::destroyEditor(QWidget *editor, const QModelIndex &index) const
{
_itemsBeingEdited.remove(index);
_modifiedEditors.remove(editor);
editor->deleteLater();
}
void FilteredKeySequenceEdit::keyPressEvent(QKeyEvent *event)
{
if(event->modifiers() == Qt::NoModifier) {
switch(event->key()) {
case Qt::Key_Enter:
case Qt::Key_Return:
emit editingFinished();
return;
case Qt::Key_Backspace:
case Qt::Key_Delete:
clear();
emit editingFinished();
event->accept();
return;
default:
event->accept();
return;
}
}
QKeySequenceEdit::keyPressEvent(event);
}
void StyledBackgroundPainter::drawBackground(QPainter* painter, const QStyleOptionViewItem& option,
const QModelIndex&)
{
const auto* opt = qstyleoption_cast<const QStyleOptionViewItem*>(&option);
const QWidget* widget = opt != nullptr ? opt->widget : nullptr;
QStyle* style = widget != nullptr ? widget->style() : QApplication::style();
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, widget);
}
#ifndef PROFILESHORTCUTDELEGATE_H
#define PROFILESHORTCUTDELEGATE_H
#include <QStyledItemDelegate>
#include <QModelIndex>
#include <QSet>
#include <QKeySequenceEdit>
class QWidget;
class QKeyEvent;
class QPainter;
namespace Konsole
{
class StyledBackgroundPainter
{
public:
static void drawBackground(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index);
};
class FilteredKeySequenceEdit: public QKeySequenceEdit
{
Q_OBJECT
public:
explicit FilteredKeySequenceEdit(QWidget *parent = nullptr): QKeySequenceEdit(parent) {}
protected:
void keyPressEvent(QKeyEvent *event) override;
};
class ShortcutItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit ShortcutItemDelegate(QObject *parent = nullptr);
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const override;
QWidget *createEditor(QWidget *aParent, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
void destroyEditor(QWidget *editor, const QModelIndex &index) const override;
private Q_SLOTS:
void editorModified();
private:
mutable QSet<QWidget *> _modifiedEditors;
mutable QSet<QModelIndex> _itemsBeingEdited;
};
}
#endif
......@@ -9,6 +9,7 @@ STATIC
ProfileReader.cpp
ProfileWriter.cpp
ProfileManager.cpp
ProfileModel.cpp
${konsoleprofile_SRCS}
)
......
#include "ProfileModel.h"
#include "ProfileManager.h"
#include "Profile.h"
#include <KLocalizedString>
#include <QIcon>
using namespace Konsole;
ProfileModel::ProfileModel(QObject *parent)
: QAbstractTableModel(parent)
{
connect(ProfileManager::instance(), &ProfileManager::profileAdded,
this, &ProfileModel::add);
connect(ProfileManager::instance(), &ProfileManager::profileRemoved,
this, &ProfileModel::remove);
connect(ProfileManager::instance(), &ProfileManager::profileChanged,
this, &ProfileModel::update);
connect(ProfileManager::instance(), &ProfileManager::favoriteStatusChanged,
this, &ProfileModel::update);
}
int ProfileModel::rowCount(const QModelIndex &unused) const
{
Q_UNUSED(unused);
return m_profiles.count();
}
int ProfileModel::columnCount(const QModelIndex& unused) const
{
Q_UNUSED(unused);
return COLUMNS;
}
QVariant ProfileModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Vertical) {
return {};
}
if (role != Qt::DisplayRole) {
return {};
}
switch(section) {
case NAME: return i18nc("@title:column Profile name", "Name");
case SHORTCUT: return i18nc("@title:column Profile keyboard shortcut", "Shortcut");
}
return {};
}
QVariant ProfileModel::data(const QModelIndex& idx, int role) const
{
if (!idx.isValid()) {
return {};
}
QExplicitlySharedDataPointer<Profile> profile = m_profiles.at(idx.row());
const auto isEnabled = ProfileManager::instance()->findFavorites().contains(profile);
switch (idx.column()) {
case VISIBILITY: {
switch(role) {
case Qt::ToolTipRole:
return i18nc("@info:tooltip List item's checkbox for making item (profile) visible in a menu",
"Show profile in menu");
case Qt::DecorationRole:
return QIcon::fromTheme(QStringLiteral("visibility"));
case Qt::CheckStateRole:
return isEnabled ? Qt::Checked : Qt::Unchecked;
}
}
break;
break;
case NAME: {
switch (role) {
case Qt::DisplayRole: return QStringLiteral("%1%2").arg(profile->name(), (idx.row() == 0 ? i18n("(Default)") : QString()));
case Qt::DecorationRole: return profile->icon();
case Qt::FontRole: {
// Default Profile
if (idx.row() == 0) {
QFont font;
font.setItalic(true);
return font;
}
}
}
}
break;
case SHORTCUT: {
switch (role) {
case Qt::DisplayRole: return ProfileManager::instance()->shortcut(profile).toString();
case Qt::ToolTipRole: return isEnabled
? i18nc("@info:tooltip", "Double click to change shortcut")
: i18nc("@info:tooltip", "Shortcut won't work while the profile is not marked as visible.");
}
break;
}
case PROFILE: {
switch(role) {
case ProfilePtrRole: return QVariant::fromValue(profile);
}
break;
}
}
return {};
}
Qt::ItemFlags ProfileModel::flags(const QModelIndex& idx) const
{
auto currentFlags = QAbstractTableModel::flags(idx);
switch(idx.column()) {
case VISIBILITY : return currentFlags | Qt::ItemIsUserCheckable;
case NAME: return currentFlags & (~Qt::ItemIsEditable);
case SHORTCUT: {
QExplicitlySharedDataPointer<Profile> profile = m_profiles.at(idx.row());
const auto isEnabled = ProfileManager::instance()->findFavorites().contains(profile);
if (!isEnabled) {
return currentFlags & (~Qt::ItemIsEnabled);
}
} break;
default: return currentFlags;
}
return currentFlags;
}
bool ProfileModel::setData(const QModelIndex &idx, const QVariant &value, int role)
{
if (!idx.isValid()) {
return false;
}
if (idx.row() != VISIBILITY || idx.row() != SHORTCUT) {
return false;
}
if (role != Qt::EditRole) {
return false;
}
auto profile = m_profiles.at(idx.row());
if (idx.row() == VISIBILITY) {
profile->setHidden(value.toBool());
emit dataChanged(idx, idx, {Qt::CheckStateRole});
} else if (idx.row() == SHORTCUT) {
QKeySequence sequence = QKeySequence::fromString(value.toString());
ProfileManager::instance()->setShortcut(profile, sequence);
emit dataChanged(idx, idx, {Qt::DisplayRole});
}
return true;
}
void ProfileModel::populate()
{
beginResetModel();
m_profiles = ProfileManager::instance()->allProfiles();
ProfileManager::instance()->sortProfiles(m_profiles);
m_profiles.prepend(ProfileManager::instance()->defaultProfile());
endResetModel();
}
void ProfileModel::add(QExplicitlySharedDataPointer<Konsole::Profile> profile)
{
// The model is too small for this to matter.
Q_UNUSED(profile);
populate();
}
void ProfileModel::remove(QExplicitlySharedDataPointer<Konsole::Profile> profile)
{
// The model is too small for this to matter.
Q_UNUSED(profile);
populate();
}
void ProfileModel::update(QExplicitlySharedDataPointer<Konsole::Profile> profile)
{
int row = m_profiles.indexOf(profile);
dataChanged(index(row, 0), index(row, COLUMNS-1));
}
#ifndef PROFILE_MODEL_H
#define PROFILE_MODEL_H
#include <QAbstractTableModel>
#include <QExplicitlySharedDataPointer>
#include <QList>
namespace Konsole {
class Profile;
class ProfileModel : public QAbstractTableModel {
Q_OBJECT
public:
enum Roles { ProfilePtrRole = Qt::UserRole + 1 };
enum Column { VISIBILITY, NAME, SHORTCUT, PROFILE, COLUMNS };
ProfileModel(QObject *parent);
void populate();
void add(QExplicitlySharedDataPointer<Profile> profile);
void remove(QExplicitlySharedDataPointer<Profile> profile);
void update(QExplicitlySharedDataPointer<Profile> profile);
int rowCount(const QModelIndex& parent) const override;
int columnCount(const QModelIndex& parent) const override;
QVariant data(const QModelIndex& idx, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
private:
QList<QExplicitlySharedDataPointer<Profile>> m_profiles;
};
}
#endif
......@@ -28,21 +28,24 @@
// Konsole
#include "profile/ProfileManager.h"
#include "profile/ProfileModel.h"
#include "session/Session.h"
#include "widgets/EditProfileDialog.h"
#include "widgets/TerminalDisplay.h"
#include "session/SessionManager.h"
#include "session/SessionController.h"
#include "delegates/ProfileShortcutDelegate.h"
using namespace Konsole;
ProfileSettings::ProfileSettings(QWidget* parent)
: QWidget(parent)
, _sessionModel(new QStandardItemModel(this))
, m_profileModel(new ProfileModel(this))
{
setupUi(this);
profilesList->setItemDelegateForColumn(ShortcutColumn, new ShortcutItemDelegate(this));
profilesList->setModel(m_profileModel);
profilesList->setItemDelegateForColumn(ProfileModel::SHORTCUT, new ShortcutItemDelegate(this));
// double clicking the profile name opens the profile edit dialog
connect(profilesList, &QAbstractItemView::doubleClicked, this, &Konsole::ProfileSettings::doubleClicked);
......@@ -50,12 +53,6 @@ ProfileSettings::ProfileSettings(QWidget* parent)
// populate the table with profiles
populateTable();
// listen for changes to profiles
connect(ProfileManager::instance(), &Konsole::ProfileManager::profileAdded, this, &Konsole::ProfileSettings::addItems);
connect(ProfileManager::instance(), &Konsole::ProfileManager::profileRemoved, this, &Konsole::ProfileSettings::removeItems);
connect(ProfileManager::instance(), &Konsole::ProfileManager::profileChanged, this, &Konsole::ProfileSettings::updateItems);
connect(ProfileManager::instance(), &Konsole::ProfileManager::favoriteStatusChanged, this, &Konsole::ProfileSettings::updateFavoriteStatus);
// setup buttons
connect(newProfileButton, &QPushButton::clicked, this, &Konsole::ProfileSettings::createProfile);
connect(editProfileButton, &QPushButton::clicked, this, &Konsole::ProfileSettings::editSelected);
......@@ -71,142 +68,16 @@ void ProfileSettings::slotAccepted()
deleteLater();
}
void ProfileSettings::itemDataChanged(QStandardItem* item)
{
if (item->column() == ShortcutColumn) {
QKeySequence sequence = QKeySequence::fromString(item->text());
QStandardItem *idItem = item->model()->item(item->row(), ProfileColumn);
ProfileManager::instance()->setShortcut(idItem->data(ProfilePtrRole).value<Profile::Ptr>(),
sequence);
} else if (item->column() == FavoriteStatusColumn) {
QStandardItem *idItem = item->model()->item(item->row(), ProfileColumn);
const bool isFavorite = item->checkState() == Qt::Checked;
ProfileManager::instance()->setFavorite(idItem->data(ProfilePtrRole).value<Profile::Ptr>(),
isFavorite);
updateShortcutField(item->model()->item(item->row(), ShortcutColumn), isFavorite);
}
}
void ProfileSettings::updateShortcutField(QStandardItem *item, bool isFavorite) const
{
if(isFavorite) {
item->setToolTip(i18nc("@info:tooltip", "Double click to change shortcut"));
item->setForeground(palette().color(QPalette::Normal, QPalette::Text));
} else {
item->setToolTip(i18nc("@info:tooltip", "Shortcut won't work while the profile is not marked as visible."));
item->setForeground(palette().color(QPalette::Disabled, QPalette::Text));
}
}
int ProfileSettings::rowForProfile(const Profile::Ptr &profile) const
void ProfileSettings::doubleClicked(const QModelIndex &idx)
{
const int rowCount = _sessionModel->rowCount();
for (int i = 0; i < rowCount; i++) {
if (_sessionModel->item(i, ProfileColumn)->data(ProfilePtrRole)
.value<Profile::Ptr>() == profile) {
return i;
}
}
return -1;
}
void ProfileSettings::removeItems(const Profile::Ptr &profile)
{
int row = rowForProfile(profile);
if (row < 0) {
return;
}
_sessionModel->removeRow(row);
}
void ProfileSettings::updateItems(const Profile::Ptr &profile)
{
const int row = rowForProfile(profile);
if (row < 0) {
return;
}
const auto items = QList<QStandardItem*> {
_sessionModel->item(row, FavoriteStatusColumn),
_sessionModel->item(row, ProfileNameColumn),
_sessionModel->item(row, ShortcutColumn),
_sessionModel->item(row, ProfileColumn),
};
updateItemsForProfile(profile, items);
}
void ProfileSettings::updateItemsForProfile(const Profile::Ptr &profile, const QList<QStandardItem*>& items) const
{
// "Enabled" checkbox
const auto isEnabled = ProfileManager::instance()->findFavorites().contains(profile);
items[FavoriteStatusColumn]->setCheckState(isEnabled ? Qt::Checked : Qt::Unchecked);
items[FavoriteStatusColumn]->setCheckable(true);
items[FavoriteStatusColumn]->setToolTip(
i18nc("@info:tooltip List item's checkbox for making item (profile) visible in a menu",
"Show profile in menu"));
// Profile Name
items[ProfileNameColumn]->setText(profile->name());
if (!profile->icon().isEmpty()) {
items[ProfileNameColumn]->setIcon(QIcon::fromTheme(profile->icon()));
}
// only allow renaming the profile from the edit profile dialog
// so as to use ProfileManager::checkProfileName()
items[ProfileNameColumn]->setEditable(false);
// Shortcut
const auto shortcut = ProfileManager::instance()->shortcut(profile).toString();
items[ShortcutColumn]->setText(shortcut);
updateShortcutField(items[ShortcutColumn], isEnabled);
// Profile ID (pointer to profile) - intended to be hidden in a view
items[ProfileColumn]->setData(QVariant::fromValue(profile), ProfilePtrRole);
}
void ProfileSettings::doubleClicked(const QModelIndex &index)
{
QStandardItem *item = _sessionModel->itemFromIndex(index);
if (item->column() == ProfileNameColumn) {
if (idx.column() == ProfileModel::NAME) {
editSelected();
}
}
void ProfileSettings::addItems(const Profile::Ptr &profile)
{
if (profile->isHidden()) {
return;
}
// each _sessionModel row has three items.
const auto items = QList<QStandardItem*> {
new QStandardItem(),
new QStandardItem(),
new QStandardItem(),
new QStandardItem(),
};
updateItemsForProfile(profile, items);
_sessionModel->appendRow(items);
}
void ProfileSettings::populateTable()
{
Q_ASSERT(!profilesList->model());
profilesList->setModel(_sessionModel);
_sessionModel->clear();
// setup session table
_sessionModel->setHorizontalHeaderLabels({
QString(), // set using header item below
i18nc("@title:column Profile name", "Name"),
i18nc("@title:column Profile keyboard shortcut", "Shortcut"),
QString(),
});
auto *favoriteColumnHeaderItem = new QStandardItem();
favoriteColumnHeaderItem->setIcon(QIcon::fromTheme(QStringLiteral("visibility")));
favoriteColumnHeaderItem->setToolTip(
i18nc("@info:tooltip List item's checkbox for making item (profile) visible in a menu",
"Show profile in menu"));
_sessionModel->setHorizontalHeaderItem(FavoriteStatusColumn, favoriteColumnHeaderItem);