Files
konsole/src/settings/ProfileSettings.cpp
Ahmad Samir 6cb301bdbe ProfileManager dialog: improve handling profiles
- Differentiate between a profile being deleteable and writable:
  - Disable the Edit key for read-only profiles, i.e. a '.profile' that
    doesn't have write permissions for the user
  - Disable the Delete key for a '.profile' that is in a directory that
    isn't writable for the user (i.e. a .profile can be read-only, but
    still deleteable by the user)

- Change the model selection mode to single selection, that simplifies
  the code, besides it looks like editing more than one profile at the
  same time hasn't worked for a while, and there are no complaints AFAICS;
  remove the now redundant selectedProfiles() method.
2021-03-02 14:54:46 +00:00

226 lines
7.9 KiB
C++

/*
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
// Own
#include "ProfileSettings.h"
// Qt
#include <QFileInfo>
#include <QStandardPaths>
#include <QStandardItem>
#include <QKeyEvent>
// Konsole
#include "profile/ProfileManager.h"
#include "profile/ProfileModel.h"
#include "session/Session.h"
#include "widgets/EditProfileDialog.h"
#include "terminalDisplay/TerminalDisplay.h"
#include "session/SessionManager.h"
#include "session/SessionController.h"
#include "delegates/ProfileShortcutDelegate.h"
using namespace Konsole;
ProfileSettings::ProfileSettings(QWidget* parent)
: QWidget(parent)
{
setupUi(this);
profilesList->setModel(ProfileModel::instance());
profilesList->setItemDelegateForColumn(ProfileModel::SHORTCUT, new ShortcutItemDelegate(this));
profilesList->setSelectionMode(QAbstractItemView::SingleSelection);
// double clicking the profile name opens the profile edit dialog
connect(profilesList, &QAbstractItemView::doubleClicked, this, &Konsole::ProfileSettings::doubleClicked);
// populate the table with profiles
populateTable();
// setup buttons
newProfileButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
editProfileButton->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
deleteProfileButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
setAsDefaultButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok-apply")));
connect(newProfileButton, &QPushButton::clicked, this, &Konsole::ProfileSettings::createProfile);
connect(editProfileButton, &QPushButton::clicked, this, &Konsole::ProfileSettings::editSelected);
connect(deleteProfileButton, &QPushButton::clicked, this, &Konsole::ProfileSettings::deleteSelected);
connect(setAsDefaultButton, &QPushButton::clicked, this, &Konsole::ProfileSettings::setSelectedAsDefault);
}
ProfileSettings::~ProfileSettings() = default;
void ProfileSettings::slotAccepted()
{
ProfileManager::instance()->saveSettings();
deleteLater();
}
void ProfileSettings::doubleClicked(const QModelIndex &idx)
{
if (idx.column() == ProfileModel::NAME) {
editSelected();
}
}
void ProfileSettings::populateTable()
{
QStyleOptionViewItem opt;
opt.features = QStyleOptionViewItem::HasCheckIndicator | QStyleOptionViewItem::HasDecoration;
auto *listHeader = profilesList->header();
profilesList->resizeColumnToContents(ProfileModel::NAME);
listHeader->setSectionResizeMode(ProfileModel::NAME, QHeaderView::ResizeMode::Stretch);
listHeader->setSectionResizeMode(ProfileModel::SHORTCUT, QHeaderView::ResizeMode::ResizeToContents);
listHeader->setStretchLastSection(false);
listHeader->setSectionsMovable(false);
profilesList->hideColumn(ProfileModel::PROFILE);
// listen for changes in the table selection and update the state of the form's buttons
// accordingly.
//
// it appears that the selection model is changed when the model itself is replaced,
// so the signals need to be reconnected each time the model is updated.
connect(profilesList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Konsole::ProfileSettings::tableSelectionChanged);
}
void ProfileSettings::tableSelectionChanged(const QItemSelection&)
{
const auto profile = currentProfile();
const bool isNotDefault = profile != ProfileManager::instance()->defaultProfile();
newProfileButton->setEnabled(true);
// See comment about isProfileWritable(profile) in editSelected()
editProfileButton->setEnabled(isProfileWritable(profile));
// Do not allow the current default profile of the session to be removed
deleteProfileButton->setEnabled(isNotDefault && isProfileDeletable(profile));
setAsDefaultButton->setEnabled(isNotDefault);
}
void ProfileSettings::deleteSelected()
{
const auto profile = currentProfile();
// The "Delete" button is disabled for the current default profile
Q_ASSERT(profile != ProfileManager::instance()->defaultProfile());
ProfileManager::instance()->deleteProfile(profile);
}
void ProfileSettings::setSelectedAsDefault()
{
ProfileManager::instance()->setDefaultProfile(currentProfile());
// do not allow the new default session type to be removed
deleteProfileButton->setEnabled(false);
setAsDefaultButton->setEnabled(false);
}
void ProfileSettings::createProfile()
{
// setup a temporary profile which is a clone of the selected profile
// or the default if no profile is selected
Profile::Ptr sourceProfile = currentProfile() ? currentProfile() : ProfileManager::instance()->defaultProfile();
Q_ASSERT(sourceProfile);
auto newProfile = Profile::Ptr(new Profile(ProfileManager::instance()->fallbackProfile()));
newProfile->clone(sourceProfile, true);
// TODO: add number suffix when the name is taken
newProfile->setProperty(Profile::Name, i18nc("@item This will be used as part of the file name", "New Profile"));
newProfile->setProperty(Profile::UntranslatedName, QStringLiteral("New Profile"));
newProfile->setProperty(Profile::MenuIndex, QStringLiteral("0"));
// Consider https://blogs.kde.org/2009/03/26/how-crash-almost-every-qtkde-application-and-how-fix-it-0 before changing the below
QPointer<EditProfileDialog> dialog = new EditProfileDialog(this);
dialog.data()->setProfile(newProfile);
dialog.data()->selectProfileName();
if (dialog.data()->exec() == QDialog::Accepted) {
ProfileManager::instance()->addProfile(newProfile);
ProfileManager::instance()->changeProfile(newProfile, newProfile->setProperties());
}
delete dialog.data();
}
void ProfileSettings::editSelected()
{
const auto profile = currentProfile();
// Read-only profiles, i.e. oens with .profile's that aren't writable
// for the user aren't editable, only clone-able by using the "New"
// button, this includes the Default/fallback profile, which is hardcoded.
if (!isProfileWritable(profile)) {
return;
}
EditProfileDialog *profileDialog = nullptr;
const auto sessionsList = SessionManager::instance()->sessions();
for (const Session *session : sessionsList) {
for (TerminalDisplay *terminalDisplay : session->views()) {
// Searching for already open EditProfileDialog instances
// for this profile
profileDialog = terminalDisplay->sessionController()->profileDialogPointer();
if (profileDialog == nullptr) {
continue;
}
if (profile->name() == profileDialog->lookupProfile()->name()
&& profileDialog->isVisible()) {
// close opened edit dialog
profileDialog->close();
}
}
}
EditProfileDialog dialog(this);
dialog.setProfile(profile);
dialog.exec();
}
Profile::Ptr ProfileSettings::currentProfile() const
{
QItemSelectionModel* selection = profilesList->selectionModel();
if ((selection == nullptr) || selection->selectedRows().count() != 1) {
return Profile::Ptr();
}
return selection
->selectedIndexes()
.at(ProfileModel::PROFILE)
.data(ProfileModel::ProfilePtrRole)
.value<Profile::Ptr>();
}
bool ProfileSettings::isProfileDeletable(Profile::Ptr profile) const
{
if (!profile || profile->isFallback()) {
return false;
}
const QFileInfo fileInfo(profile->path());
return fileInfo.exists()
&& QFileInfo(fileInfo.path()).isWritable(); // To delete a file, parent dir must be writable
}
bool ProfileSettings::isProfileWritable(Profile::Ptr profile) const
{
return profile
&& !profile->isFallback() // Default/fallback profile is hardcoded
&& QFileInfo(profile->path()).isWritable();
}
void ProfileSettings::setShortcutEditorVisible(bool visible)
{
profilesList->setColumnHidden(ProfileModel::SHORTCUT, !visible);
}