ProfileSettings.cpp 7.73 KB
Newer Older
1
/*
2
3
4
    SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>

    SPDX-License-Identifier: GPL-2.0-or-later
5
6
*/

7
// Own
8
#include "ProfileSettings.h"
9

10
// Qt
11
12
#include <QFileInfo>
#include <QStandardPaths>
13
#include <QStandardItem>
14
#include <QKeyEvent>
15
16

// Konsole
17
#include "profile/ProfileManager.h"
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
18
#include "profile/ProfileModel.h"
19
#include "session/Session.h"
20
#include "widgets/EditProfileDialog.h"
21
#include "terminalDisplay/TerminalDisplay.h"
22
23
#include "session/SessionManager.h"
#include "session/SessionController.h"
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
24
#include "delegates/ProfileShortcutDelegate.h"
25
26
27

using namespace Konsole;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
28
29
ProfileSettings::ProfileSettings(QWidget* parent)
    : QWidget(parent)
30
{
31
    setupUi(this);
32

33
    profilesList->setModel(ProfileModel::instance());
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
34
    profilesList->setItemDelegateForColumn(ProfileModel::SHORTCUT, new ShortcutItemDelegate(this));
35
    profilesList->setSelectionMode(QAbstractItemView::SingleSelection);
36

37
    // double clicking the profile name opens the profile edit dialog
38
    connect(profilesList, &QAbstractItemView::doubleClicked, this, &Konsole::ProfileSettings::doubleClicked);
39

40
41
42
    // populate the table with profiles
    populateTable();

43
    // setup buttons
44
45
46
47
48
    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")));

49
50
51
52
    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);
53
}
54

55
ProfileSettings::~ProfileSettings() = default;
56

57
void ProfileSettings::slotAccepted()
Laurent Montel's avatar
Laurent Montel committed
58
59
60
61
62
{
    ProfileManager::instance()->saveSettings();
    deleteLater();
}

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
63
void ProfileSettings::doubleClicked(const QModelIndex &idx)
64
{
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
65
    if (idx.column() == ProfileModel::NAME) {
66
67
68
69
        editSelected();
    }
}

70
void ProfileSettings::populateTable()
71
{
72
73
74
75
    QStyleOptionViewItem opt;
    opt.features = QStyleOptionViewItem::HasCheckIndicator | QStyleOptionViewItem::HasDecoration;
    auto *listHeader = profilesList->header();

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
76
77
78
79
    profilesList->resizeColumnToContents(ProfileModel::NAME);

    listHeader->setSectionResizeMode(ProfileModel::NAME, QHeaderView::ResizeMode::Stretch);
    listHeader->setSectionResizeMode(ProfileModel::SHORTCUT, QHeaderView::ResizeMode::ResizeToContents);
80
81
82
    listHeader->setStretchLastSection(false);
    listHeader->setSectionsMovable(false);

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
83
    profilesList->hideColumn(ProfileModel::PROFILE);
84

85
86
87
88
89
    // 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.
90
    connect(profilesList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Konsole::ProfileSettings::tableSelectionChanged);
91
}
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
92

93
void ProfileSettings::tableSelectionChanged(const QItemSelection&)
94
{
95
96
97
98
99
100
101
    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));
102

103
104
105
106
    // Do not allow the current default profile of the session to be removed
    deleteProfileButton->setEnabled(isNotDefault && isProfileDeletable(profile));

    setAsDefaultButton->setEnabled(isNotDefault);
107
}
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
108

109
void ProfileSettings::deleteSelected()
110
{
111
112
113
114
115
116
    const auto profile = currentProfile();

    // The "Delete" button is disabled for the current default profile
    Q_ASSERT(profile != ProfileManager::instance()->defaultProfile());

    ProfileManager::instance()->deleteProfile(profile);
117
}
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
118

119
void ProfileSettings::setSelectedAsDefault()
120
{
121
    ProfileManager::instance()->setDefaultProfile(currentProfile());
122
    // do not allow the new default session type to be removed
123
124
    deleteProfileButton->setEnabled(false);
    setAsDefaultButton->setEnabled(false);
125
}
126

127
void ProfileSettings::createProfile()
128
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
129
    // setup a temporary profile which is a clone of the selected profile
130
    // or the default if no profile is selected
131
    Profile::Ptr sourceProfile = currentProfile() ? currentProfile() : ProfileManager::instance()->defaultProfile();
132

Kurt Hindenburg's avatar
Kurt Hindenburg committed
133
    Q_ASSERT(sourceProfile);
134

135
    auto newProfile = Profile::Ptr(new Profile(ProfileManager::instance()->fallbackProfile()));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
136
    newProfile->clone(sourceProfile, true);
137
138
139
    const QString uniqueName = ProfileManager::instance()->generateUniqueName();
    newProfile->setProperty(Profile::Name, uniqueName);
    newProfile->setProperty(Profile::UntranslatedName, uniqueName);
140

141
    // Consider https://blogs.kde.org/2009/03/26/how-crash-almost-every-qtkde-application-and-how-fix-it-0 before changing the below
142
143
144
    QPointer<EditProfileDialog> dialog = new EditProfileDialog(this);
    dialog.data()->setProfile(newProfile);
    dialog.data()->selectProfileName();
Robert Knight's avatar
   
Robert Knight committed
145

146
    if (dialog.data()->exec() == QDialog::Accepted) {
147
148
        ProfileManager::instance()->addProfile(newProfile);
        ProfileManager::instance()->changeProfile(newProfile, newProfile->setProperties());
Robert Knight's avatar
   
Robert Knight committed
149
    }
150
    delete dialog.data();
151
}
152
void ProfileSettings::editSelected()
153
{
154
155
156
157
158
159
160
161
162
    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;
    }

163
    EditProfileDialog *profileDialog = nullptr;
164
165
    const auto sessionsList = SessionManager::instance()->sessions();
    for (const Session *session : sessionsList) {
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
166
        for (TerminalDisplay *terminalDisplay : session->views()) {
167
168
            // Searching for already open EditProfileDialog instances
            // for this profile
169
            profileDialog = terminalDisplay->sessionController()->profileDialogPointer();
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
170
171
172
            if (profileDialog == nullptr) {
                continue;
            }
173
174
175
176
177

            if (profile->name() == profileDialog->lookupProfile()->name()
                && profileDialog->isVisible()) {
                // close opened edit dialog
                profileDialog->close();
178
            }
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
179
        }
180
181
    }

182
    EditProfileDialog dialog(this);
183

184
    dialog.setProfile(profile);
185
186
    dialog.exec();
}
187

188
Profile::Ptr ProfileSettings::currentProfile() const
189
{
190
    QItemSelectionModel* selection = profilesList->selectionModel();
191

Kurt Hindenburg's avatar
Kurt Hindenburg committed
192
    if ((selection == nullptr) || selection->selectedRows().count() != 1) {
193
        return Profile::Ptr();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
194
    }
195

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
196
197
198
199
200
    return selection
        ->selectedIndexes()
        .at(ProfileModel::PROFILE)
        .data(ProfileModel::ProfilePtrRole)
        .value<Profile::Ptr>();
201
}
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
202

203
bool ProfileSettings::isProfileDeletable(Profile::Ptr profile) const
204
{
205
    if (!profile || profile->isFallback()) {
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
206
        return false;
207
    }
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
208
209

    const QFileInfo fileInfo(profile->path());
210
211
212
    return fileInfo.exists()
        && QFileInfo(fileInfo.path()).isWritable(); // To delete a file, parent dir must be writable
}
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
213

214
215
216
217
218
bool ProfileSettings::isProfileWritable(Profile::Ptr profile) const
{
    return profile
        && !profile->isFallback() // Default/fallback profile is hardcoded
        && QFileInfo(profile->path()).isWritable();
219
}
220

Tomaz  Canabrava's avatar
Tomaz Canabrava committed
221
void ProfileSettings::setShortcutEditorVisible(bool visible)
222
{
Tomaz  Canabrava's avatar
Tomaz Canabrava committed
223
    profilesList->setColumnHidden(ProfileModel::SHORTCUT, !visible);
224
}