Changeset View
Changeset View
Standalone View
Standalone View
src/settings/ProfileSettings.cpp
Show All 13 Lines | 1 | /* | |||
---|---|---|---|---|---|
14 | You should have received a copy of the GNU General Public License | 14 | You should have received a copy of the GNU General Public License | ||
15 | along with this program; if not, write to the Free Software | 15 | along with this program; if not, write to the Free Software | ||
16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
17 | 02110-1301 USA. | 17 | 02110-1301 USA. | ||
18 | */ | 18 | */ | ||
19 | 19 | | |||
20 | // Own | 20 | // Own | ||
21 | #include "ProfileSettings.h" | 21 | #include "ProfileSettings.h" | ||
22 | #include <QPainter> | ||||
22 | 23 | | |||
23 | // Qt | 24 | // Qt | ||
24 | #include <QFileInfo> | 25 | #include <QFileInfo> | ||
25 | #include <QStandardPaths> | 26 | #include <QStandardPaths> | ||
26 | #include <QStandardItem> | 27 | #include <QStandardItem> | ||
28 | #include <QKeyEvent> | ||||
27 | 29 | | |||
28 | // KDE | 30 | // KDE | ||
29 | #include <KKeySequenceWidget> | | |||
30 | #include <KLocalizedString> | 31 | #include <KLocalizedString> | ||
31 | #include <KIconLoader> | 32 | #include <KIconLoader> | ||
32 | #include <QPushButton> | 33 | #include <QPushButton> | ||
33 | 34 | | |||
34 | // Konsole | 35 | // Konsole | ||
35 | #include "EditProfileDialog.h" | 36 | #include "EditProfileDialog.h" | ||
36 | #include "ProfileManager.h" | 37 | #include "ProfileManager.h" | ||
37 | #include "Session.h" | 38 | #include "Session.h" | ||
38 | #include "TerminalDisplay.h" | 39 | #include "TerminalDisplay.h" | ||
39 | #include "SessionManager.h" | 40 | #include "SessionManager.h" | ||
40 | #include "SessionController.h" | 41 | #include "SessionController.h" | ||
41 | 42 | | |||
42 | using namespace Konsole; | 43 | using namespace Konsole; | ||
43 | 44 | | |||
44 | ProfileSettings::ProfileSettings(QWidget* aParent) | 45 | ProfileSettings::ProfileSettings(QWidget* aParent) | ||
45 | : QWidget(aParent) | 46 | : QWidget(aParent) | ||
46 | , _sessionModel(new QStandardItemModel(this)) | 47 | , _sessionModel(new QStandardItemModel(this)) | ||
47 | { | 48 | { | ||
48 | setupUi(this); | 49 | setupUi(this); | ||
49 | 50 | | |||
50 | // hide vertical header | 51 | profilesList->setItemDelegateForColumn(ShortcutColumn, new ShortcutItemDelegate(this)); | ||
51 | sessionTable->verticalHeader()->hide(); | | |||
52 | sessionTable->setShowGrid(false); | | |||
53 | | ||||
54 | sessionTable->setItemDelegateForColumn(FavoriteStatusColumn, new FavoriteItemDelegate(this)); | | |||
55 | sessionTable->setItemDelegateForColumn(ShortcutColumn, new ShortcutItemDelegate(this)); | | |||
56 | sessionTable->setEditTriggers(sessionTable->editTriggers() | QAbstractItemView::SelectedClicked); | | |||
57 | 52 | | |||
58 | // double clicking the profile name opens the profile edit dialog | 53 | // double clicking the profile name opens the profile edit dialog | ||
59 | connect(sessionTable, &QTableView::doubleClicked, this, &Konsole::ProfileSettings::doubleClicked); | 54 | connect(profilesList, &QAbstractItemView::doubleClicked, this, &Konsole::ProfileSettings::doubleClicked); | ||
60 | 55 | | |||
61 | // populate the table with profiles | 56 | // populate the table with profiles | ||
62 | populateTable(); | 57 | populateTable(); | ||
63 | 58 | | |||
64 | // listen for changes to profiles | 59 | // listen for changes to profiles | ||
65 | connect(ProfileManager::instance(), &Konsole::ProfileManager::profileAdded, this, &Konsole::ProfileSettings::addItems); | 60 | connect(ProfileManager::instance(), &Konsole::ProfileManager::profileAdded, this, &Konsole::ProfileSettings::addItems); | ||
66 | connect(ProfileManager::instance(), &Konsole::ProfileManager::profileRemoved, this, &Konsole::ProfileSettings::removeItems); | 61 | connect(ProfileManager::instance(), &Konsole::ProfileManager::profileRemoved, this, &Konsole::ProfileSettings::removeItems); | ||
67 | connect(ProfileManager::instance(), &Konsole::ProfileManager::profileChanged, this, &Konsole::ProfileSettings::updateItems); | 62 | connect(ProfileManager::instance(), &Konsole::ProfileManager::profileChanged, this, &Konsole::ProfileSettings::updateItems); | ||
68 | connect(ProfileManager::instance() , &Konsole::ProfileManager::favoriteStatusChanged, this, &Konsole::ProfileSettings::updateFavoriteStatus); | 63 | connect(ProfileManager::instance(), &Konsole::ProfileManager::favoriteStatusChanged, this, &Konsole::ProfileSettings::updateFavoriteStatus); | ||
69 | 64 | | |||
70 | // resize the session table to the full width of the table | | |||
71 | sessionTable->horizontalHeader()->setHighlightSections(false); | | |||
72 | sessionTable->horizontalHeader()->setStretchLastSection(true); | | |||
73 | sessionTable->resizeColumnsToContents(); | | |||
74 | | ||||
75 | // setup buttons | 65 | // setup buttons | ||
76 | connect(newProfileButton, &QPushButton::clicked, this, &Konsole::ProfileSettings::createProfile); | 66 | connect(newProfileButton, &QPushButton::clicked, this, &Konsole::ProfileSettings::createProfile); | ||
77 | connect(editProfileButton, &QPushButton::clicked, this, &Konsole::ProfileSettings::editSelected); | 67 | connect(editProfileButton, &QPushButton::clicked, this, &Konsole::ProfileSettings::editSelected); | ||
78 | connect(deleteProfileButton, &QPushButton::clicked, this, &Konsole::ProfileSettings::deleteSelected); | 68 | connect(deleteProfileButton, &QPushButton::clicked, this, &Konsole::ProfileSettings::deleteSelected); | ||
79 | connect(setAsDefaultButton, &QPushButton::clicked, this, &Konsole::ProfileSettings::setSelectedAsDefault); | 69 | connect(setAsDefaultButton, &QPushButton::clicked, this, &Konsole::ProfileSettings::setSelectedAsDefault); | ||
80 | } | 70 | } | ||
81 | 71 | | |||
82 | ProfileSettings::~ProfileSettings() = default; | 72 | ProfileSettings::~ProfileSettings() = default; | ||
83 | 73 | | |||
84 | void ProfileSettings::slotAccepted() | 74 | void ProfileSettings::slotAccepted() | ||
85 | { | 75 | { | ||
86 | ProfileManager::instance()->saveSettings(); | 76 | ProfileManager::instance()->saveSettings(); | ||
87 | deleteLater(); | 77 | deleteLater(); | ||
88 | } | 78 | } | ||
89 | 79 | | |||
90 | void ProfileSettings::itemDataChanged(QStandardItem* item) | 80 | void ProfileSettings::itemDataChanged(QStandardItem* item) | ||
91 | { | 81 | { | ||
92 | if (item->column() == ShortcutColumn) { | 82 | if (item->column() == ShortcutColumn) { | ||
93 | QKeySequence sequence = QKeySequence::fromString(item->text()); | 83 | QKeySequence sequence = QKeySequence::fromString(item->text()); | ||
94 | ProfileManager::instance()->setShortcut(item->data(ShortcutRole).value<Profile::Ptr>(), | 84 | QStandardItem *idItem = item->model()->item(item->row(), ProfileColumn); | ||
85 | ProfileManager::instance()->setShortcut(idItem->data(ProfilePtrRole).value<Profile::Ptr>(), | ||||
95 | sequence); | 86 | sequence); | ||
87 | } else if (item->column() == FavoriteStatusColumn) { | ||||
88 | QStandardItem *idItem = item->model()->item(item->row(), ProfileColumn); | ||||
89 | const bool isFavorite = item->checkState() == Qt::Checked; | ||||
90 | ProfileManager::instance()->setFavorite(idItem->data(ProfilePtrRole).value<Profile::Ptr>(), | ||||
91 | isFavorite); | ||||
92 | updateShortcutField(item->model()->item(item->row(), ShortcutColumn), isFavorite); | ||||
93 | } | ||||
94 | } | ||||
95 | | ||||
96 | void ProfileSettings::updateShortcutField(QStandardItem *item, bool isFavorite) const | ||||
97 | { | ||||
98 | if(isFavorite) { | ||||
99 | item->setToolTip(i18nc("@info:tooltip", "Double click to change shortcut")); | ||||
100 | item->setForeground(palette().color(QPalette::Normal, QPalette::Text)); | ||||
101 | } else { | ||||
102 | item->setToolTip(i18nc("@info:tooltip", "Shortcut won't work while the profile is not marked as visible.")); | ||||
103 | item->setForeground(palette().color(QPalette::Disabled, QPalette::Text)); | ||||
96 | } | 104 | } | ||
97 | } | 105 | } | ||
98 | 106 | | |||
99 | int ProfileSettings::rowForProfile(const Profile::Ptr &profile) const | 107 | int ProfileSettings::rowForProfile(const Profile::Ptr &profile) const | ||
100 | { | 108 | { | ||
101 | const int rowCount = _sessionModel->rowCount(); | 109 | const int rowCount = _sessionModel->rowCount(); | ||
102 | for (int i = 0; i < rowCount; i++) { | 110 | for (int i = 0; i < rowCount; i++) { | ||
103 | if (_sessionModel->item(i, ProfileNameColumn)->data(ProfileKeyRole) | 111 | if (_sessionModel->item(i, ProfileColumn)->data(ProfilePtrRole) | ||
104 | .value<Profile::Ptr>() == profile) { | 112 | .value<Profile::Ptr>() == profile) { | ||
105 | return i; | 113 | return i; | ||
106 | } | 114 | } | ||
107 | } | 115 | } | ||
108 | return -1; | 116 | return -1; | ||
109 | } | 117 | } | ||
110 | void ProfileSettings::removeItems(const Profile::Ptr &profile) | 118 | void ProfileSettings::removeItems(const Profile::Ptr &profile) | ||
111 | { | 119 | { | ||
112 | int row = rowForProfile(profile); | 120 | int row = rowForProfile(profile); | ||
113 | if (row < 0) { | 121 | if (row < 0) { | ||
114 | return; | 122 | return; | ||
115 | } | 123 | } | ||
116 | 124 | | |||
117 | _sessionModel->removeRow(row); | 125 | _sessionModel->removeRow(row); | ||
118 | } | 126 | } | ||
119 | void ProfileSettings::updateItems(const Profile::Ptr &profile) | 127 | void ProfileSettings::updateItems(const Profile::Ptr &profile) | ||
120 | { | 128 | { | ||
121 | const int row = rowForProfile(profile); | 129 | const int row = rowForProfile(profile); | ||
122 | if (row < 0) { | 130 | if (row < 0) { | ||
123 | return; | 131 | return; | ||
124 | } | 132 | } | ||
125 | 133 | | |||
126 | const auto items = QList<QStandardItem*> { | 134 | const auto items = QList<QStandardItem*> { | ||
127 | _sessionModel->item(row, ProfileNameColumn), | | |||
128 | _sessionModel->item(row, FavoriteStatusColumn), | 135 | _sessionModel->item(row, FavoriteStatusColumn), | ||
129 | _sessionModel->item(row, ShortcutColumn) | 136 | _sessionModel->item(row, ProfileNameColumn), | ||
137 | _sessionModel->item(row, ShortcutColumn), | ||||
138 | _sessionModel->item(row, ProfileColumn), | ||||
130 | }; | 139 | }; | ||
131 | updateItemsForProfile(profile, items); | 140 | updateItemsForProfile(profile, items); | ||
132 | } | 141 | } | ||
133 | void ProfileSettings::updateItemsForProfile(const Profile::Ptr &profile, const QList<QStandardItem*>& items) const | 142 | void ProfileSettings::updateItemsForProfile(const Profile::Ptr &profile, const QList<QStandardItem*>& items) const | ||
134 | { | 143 | { | ||
144 | // "Enabled" checkbox | ||||
145 | const auto isEnabled = ProfileManager::instance()->findFavorites().contains(profile); | ||||
146 | items[FavoriteStatusColumn]->setCheckState(isEnabled ? Qt::Checked : Qt::Unchecked); | ||||
147 | items[FavoriteStatusColumn]->setCheckable(true); | ||||
148 | items[FavoriteStatusColumn]->setToolTip( | ||||
149 | i18nc("@info:tooltip List item's checkbox for making item (profile) visible in a menu", | ||||
150 | "Show profile in menu")); | ||||
151 | | ||||
135 | // Profile Name | 152 | // Profile Name | ||
136 | items[ProfileNameColumn]->setText(profile->name()); | 153 | items[ProfileNameColumn]->setText(profile->name()); | ||
137 | if (!profile->icon().isEmpty()) { | 154 | if (!profile->icon().isEmpty()) { | ||
138 | items[ProfileNameColumn]->setIcon(QIcon::fromTheme(profile->icon())); | 155 | items[ProfileNameColumn]->setIcon(QIcon::fromTheme(profile->icon())); | ||
139 | } | 156 | } | ||
140 | items[ProfileNameColumn]->setData(QVariant::fromValue(profile), ProfileKeyRole); | | |||
141 | // only allow renaming the profile from the edit profile dialog | 157 | // only allow renaming the profile from the edit profile dialog | ||
142 | // so as to use ProfileManager::checkProfileName() | 158 | // so as to use ProfileManager::checkProfileName() | ||
143 | items[ProfileNameColumn]->setEditable(false); | 159 | items[ProfileNameColumn]->setEditable(false); | ||
144 | 160 | | |||
145 | // Favorite Status | | |||
146 | const auto isFavorite = ProfileManager::instance()->findFavorites().contains(profile); | | |||
147 | const auto icon = isFavorite ? QIcon::fromTheme(QStringLiteral("dialog-ok-apply")) : QIcon(); | | |||
148 | items[FavoriteStatusColumn]->setData(icon, Qt::DecorationRole); | | |||
149 | items[FavoriteStatusColumn]->setData(QVariant::fromValue(profile), ProfileKeyRole); | | |||
150 | items[FavoriteStatusColumn]->setToolTip(i18nc("@info:tooltip", "Click to toggle status")); | | |||
151 | | ||||
152 | // Shortcut | 161 | // Shortcut | ||
153 | const auto shortcut = ProfileManager::instance()->shortcut(profile).toString(); | 162 | const auto shortcut = ProfileManager::instance()->shortcut(profile).toString(); | ||
154 | items[ShortcutColumn]->setText(shortcut); | 163 | items[ShortcutColumn]->setText(shortcut); | ||
155 | items[ShortcutColumn]->setData(QVariant::fromValue(profile), ShortcutRole); | 164 | updateShortcutField(items[ShortcutColumn], isEnabled); | ||
156 | items[ShortcutColumn]->setToolTip(i18nc("@info:tooltip", "Double click to change shortcut")); | 165 | | ||
166 | // Profile ID (pointer to profile) - intended to be hidden in a view | ||||
167 | items[ProfileColumn]->setData(QVariant::fromValue(profile), ProfilePtrRole); | ||||
157 | } | 168 | } | ||
158 | 169 | | |||
159 | void ProfileSettings::doubleClicked(const QModelIndex &index) | 170 | void ProfileSettings::doubleClicked(const QModelIndex &index) | ||
160 | { | 171 | { | ||
161 | QStandardItem *item = _sessionModel->itemFromIndex(index); | 172 | QStandardItem *item = _sessionModel->itemFromIndex(index); | ||
162 | if (item->column() == ProfileNameColumn) { | 173 | if (item->column() == ProfileNameColumn) { | ||
163 | editSelected(); | 174 | editSelected(); | ||
164 | } | 175 | } | ||
165 | } | 176 | } | ||
166 | 177 | | |||
167 | void ProfileSettings::addItems(const Profile::Ptr &profile) | 178 | void ProfileSettings::addItems(const Profile::Ptr &profile) | ||
168 | { | 179 | { | ||
169 | if (profile->isHidden()) { | 180 | if (profile->isHidden()) { | ||
170 | return; | 181 | return; | ||
171 | } | 182 | } | ||
172 | 183 | | |||
173 | // each _sessionModel row has three items. | 184 | // each _sessionModel row has three items. | ||
174 | const auto items = QList<QStandardItem*> { | 185 | const auto items = QList<QStandardItem*> { | ||
175 | new QStandardItem(), | 186 | new QStandardItem(), | ||
176 | new QStandardItem(), | 187 | new QStandardItem(), | ||
177 | new QStandardItem() | 188 | new QStandardItem(), | ||
189 | new QStandardItem(), | ||||
178 | }; | 190 | }; | ||
179 | 191 | | |||
180 | updateItemsForProfile(profile, items); | 192 | updateItemsForProfile(profile, items); | ||
181 | _sessionModel->appendRow(items); | 193 | _sessionModel->appendRow(items); | ||
182 | } | 194 | } | ||
183 | void ProfileSettings::populateTable() | 195 | void ProfileSettings::populateTable() | ||
184 | { | 196 | { | ||
185 | Q_ASSERT(!sessionTable->model()); | 197 | Q_ASSERT(!profilesList->model()); | ||
186 | 198 | | |||
187 | sessionTable->setModel(_sessionModel); | 199 | profilesList->setModel(_sessionModel); | ||
188 | 200 | | |||
189 | _sessionModel->clear(); | 201 | _sessionModel->clear(); | ||
190 | // setup session table | 202 | // setup session table | ||
191 | _sessionModel->setHorizontalHeaderLabels({i18nc("@title:column Profile label", "Name"), | 203 | _sessionModel->setHorizontalHeaderLabels({ | ||
192 | i18nc("@title:column Display profile in file menu", "Show"), | 204 | QString(), // set using header item below | ||
193 | i18nc("@title:column Profile shortcut text", "Shortcut")}); | 205 | i18nc("@title:column Profile name", "Name"), | ||
206 | i18nc("@title:column Profile keyboard shortcut", "Shortcut"), | ||||
207 | QString(), | ||||
208 | }); | ||||
209 | auto *favoriteColumnHeaderItem = new QStandardItem(); | ||||
210 | favoriteColumnHeaderItem->setIcon(QIcon::fromTheme(QStringLiteral("visibility"))); | ||||
211 | favoriteColumnHeaderItem->setToolTip( | ||||
212 | i18nc("@info:tooltip List item's checkbox for making item (profile) visible in a menu", | ||||
213 | "Show profile in menu")); | ||||
214 | _sessionModel->setHorizontalHeaderItem(FavoriteStatusColumn, favoriteColumnHeaderItem); | ||||
215 | | ||||
216 | // Calculate favorite column width. resizeColumnToContents() | ||||
217 | // is not used because it takes distance between checkbox and | ||||
218 | // text into account, but there is no text and it looks weird. | ||||
219 | const int headerMargin = style()->pixelMetric(QStyle::PM_HeaderMargin, nullptr, | ||||
220 | profilesList->header()); | ||||
221 | const int iconWidth = style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, | ||||
222 | profilesList->header()); | ||||
223 | const int favoriteHeaderWidth = headerMargin * 2 + iconWidth; | ||||
224 | QStyleOptionViewItem opt; | ||||
225 | opt.features = QStyleOptionViewItem::HasCheckIndicator | QStyleOptionViewItem::HasDecoration; | ||||
226 | const QRect checkBoxRect = style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, | ||||
227 | &opt, profilesList); | ||||
228 | // When right edge is at x < 0 it is assumed the checkbox is | ||||
229 | // placed on the right item's side and the margin between right | ||||
230 | // checkbox edge and right item edge should be used. | ||||
231 | const int checkBoxMargin = checkBoxRect.right() >= 0 ? checkBoxRect.x() | ||||
232 | : 0 - checkBoxRect.right(); | ||||
233 | const int favoriteItemWidth = checkBoxMargin * 2 + checkBoxRect.width(); | ||||
234 | auto *listHeader = profilesList->header(); | ||||
235 | | ||||
236 | profilesList->setColumnWidth(FavoriteStatusColumn, | ||||
237 | qMax(favoriteHeaderWidth, favoriteItemWidth)); | ||||
238 | profilesList->resizeColumnToContents(ProfileNameColumn); | ||||
239 | listHeader->setSectionResizeMode(FavoriteStatusColumn, QHeaderView::ResizeMode::Fixed); | ||||
240 | listHeader->setSectionResizeMode(ProfileNameColumn, QHeaderView::ResizeMode::Stretch); | ||||
241 | listHeader->setSectionResizeMode(ShortcutColumn, QHeaderView::ResizeMode::ResizeToContents); | ||||
242 | listHeader->setStretchLastSection(false); | ||||
243 | listHeader->setSectionsMovable(false); | ||||
244 | | ||||
245 | profilesList->hideColumn(ProfileColumn); | ||||
194 | 246 | | |||
195 | QList<Profile::Ptr> profiles = ProfileManager::instance()->allProfiles(); | 247 | QList<Profile::Ptr> profiles = ProfileManager::instance()->allProfiles(); | ||
196 | ProfileManager::instance()->sortProfiles(profiles); | 248 | ProfileManager::instance()->sortProfiles(profiles); | ||
197 | 249 | | |||
198 | foreach(const Profile::Ptr& profile, profiles) { | 250 | foreach(const Profile::Ptr& profile, profiles) { | ||
199 | addItems(profile); | 251 | addItems(profile); | ||
200 | } | 252 | } | ||
201 | updateDefaultItem(); | 253 | updateDefaultItem(); | ||
202 | 254 | | |||
203 | connect(_sessionModel, &QStandardItemModel::itemChanged, this, &Konsole::ProfileSettings::itemDataChanged); | 255 | connect(_sessionModel, &QStandardItemModel::itemChanged, this, &Konsole::ProfileSettings::itemDataChanged); | ||
204 | 256 | | |||
205 | // listen for changes in the table selection and update the state of the form's buttons | 257 | // listen for changes in the table selection and update the state of the form's buttons | ||
206 | // accordingly. | 258 | // accordingly. | ||
207 | // | 259 | // | ||
208 | // it appears that the selection model is changed when the model itself is replaced, | 260 | // it appears that the selection model is changed when the model itself is replaced, | ||
209 | // so the signals need to be reconnected each time the model is updated. | 261 | // so the signals need to be reconnected each time the model is updated. | ||
210 | connect(sessionTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Konsole::ProfileSettings::tableSelectionChanged); | 262 | connect(profilesList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Konsole::ProfileSettings::tableSelectionChanged); | ||
211 | | ||||
212 | sessionTable->selectRow(0); | | |||
213 | } | 263 | } | ||
214 | void ProfileSettings::updateDefaultItem() | 264 | void ProfileSettings::updateDefaultItem() | ||
215 | { | 265 | { | ||
216 | Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile(); | 266 | Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile(); | ||
217 | 267 | | |||
268 | const QString defaultItemSuffix = i18nc("Default list item's name suffix (with separator)", " (default)"); | ||||
269 | | ||||
218 | const int rowCount = _sessionModel->rowCount(); | 270 | const int rowCount = _sessionModel->rowCount(); | ||
219 | for (int i = 0; i < rowCount; i++) { | 271 | for (int i = 0; i < rowCount; i++) { | ||
220 | QStandardItem* item = _sessionModel->item(i); | 272 | QStandardItem* item = _sessionModel->item(i, ProfileNameColumn); | ||
221 | QFont itemFont = item->font(); | 273 | QFont itemFont = item->font(); | ||
274 | QStandardItem* profileIdItem = _sessionModel->item(i, ProfileColumn); | ||||
275 | auto profile = profileIdItem->data().value<Profile::Ptr>(); | ||||
276 | const bool isDefault = (defaultProfile == profile); | ||||
277 | const QString cleanItemName = profile != nullptr ? profile->name() : QString(); | ||||
222 | 278 | | |||
223 | bool isDefault = (defaultProfile == item->data().value<Profile::Ptr>()); | 279 | if (isDefault) { | ||
224 | 280 | itemFont.setItalic(true); | |||
225 | if (isDefault && !itemFont.bold()) { | | |||
226 | QIcon icon(KIconLoader::global()->loadIcon(defaultProfile->icon(), KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(QStringLiteral("emblem-favorite")))); | | |||
227 | item->setIcon(icon); | | |||
228 | itemFont.setBold(true); | | |||
229 | item->setFont(itemFont); | 281 | item->setFont(itemFont); | ||
230 | } else if (!isDefault && itemFont.bold()) { | 282 | item->setText(cleanItemName + defaultItemSuffix); | ||
231 | QModelIndex index = _sessionModel->index(i, ProfileNameColumn); | 283 | } else if (!isDefault) { | ||
232 | Profile::Ptr profile = index.data(ProfileSettings::ProfileKeyRole).value<Profile::Ptr>(); | 284 | // FIXME: use default font | ||
233 | if (profile != nullptr) { | 285 | itemFont.setItalic(false); | ||
234 | item->setIcon(QIcon::fromTheme(profile->icon())); | | |||
235 | } | | |||
236 | itemFont.setBold(false); | | |||
237 | item->setFont(itemFont); | 286 | item->setFont(itemFont); | ||
287 | item->setText(cleanItemName); | ||||
238 | } | 288 | } | ||
239 | } | 289 | } | ||
240 | } | 290 | } | ||
241 | void ProfileSettings::tableSelectionChanged(const QItemSelection&) | 291 | void ProfileSettings::tableSelectionChanged(const QItemSelection&) | ||
242 | { | 292 | { | ||
243 | const int selectedRows = sessionTable->selectionModel()->selectedRows().count(); | | |||
244 | const ProfileManager* manager = ProfileManager::instance(); | 293 | const ProfileManager* manager = ProfileManager::instance(); | ||
245 | const bool isNotDefault = (selectedRows > 0) && currentProfile() != manager->defaultProfile(); | 294 | bool isNotDefault = true; | ||
246 | const bool isDeletable = (selectedRows > 1) || | 295 | bool isDeletable = true; | ||
247 | (selectedRows == 1 && isProfileDeletable(currentProfile())); | 296 | | ||
297 | const auto profiles = selectedProfiles(); | ||||
298 | for (const auto &profile: profiles) { | ||||
299 | isNotDefault = isNotDefault && (profile != manager->defaultProfile()); | ||||
300 | isDeletable = isDeletable && isProfileDeletable(profile); | ||||
301 | } | ||||
248 | 302 | | |||
249 | newProfileButton->setEnabled(selectedRows < 2); | 303 | newProfileButton->setEnabled(profiles.count() < 2); | ||
250 | // FIXME: At some point editing 2+ profiles no longer works | 304 | // FIXME: At some point editing 2+ profiles no longer works | ||
251 | editProfileButton->setEnabled(selectedRows == 1); | 305 | editProfileButton->setEnabled(profiles.count() == 1); | ||
252 | // do not allow the default session type to be removed | 306 | // do not allow the default session type to be removed | ||
253 | deleteProfileButton->setEnabled(isDeletable && isNotDefault); | 307 | deleteProfileButton->setEnabled(isDeletable && isNotDefault && (profiles.count() > 0)); | ||
254 | setAsDefaultButton->setEnabled(isNotDefault && (selectedRows < 2)); | 308 | setAsDefaultButton->setEnabled(isNotDefault && (profiles.count() == 1)); | ||
255 | } | 309 | } | ||
256 | void ProfileSettings::deleteSelected() | 310 | void ProfileSettings::deleteSelected() | ||
257 | { | 311 | { | ||
258 | foreach(const Profile::Ptr & profile, selectedProfiles()) { | 312 | foreach(const Profile::Ptr & profile, selectedProfiles()) { | ||
259 | if (profile != ProfileManager::instance()->defaultProfile()) { | 313 | if (profile != ProfileManager::instance()->defaultProfile()) { | ||
260 | ProfileManager::instance()->deleteProfile(profile); | 314 | ProfileManager::instance()->deleteProfile(profile); | ||
261 | } | 315 | } | ||
262 | } | 316 | } | ||
263 | } | 317 | } | ||
264 | void ProfileSettings::setSelectedAsDefault() | 318 | void ProfileSettings::setSelectedAsDefault() | ||
265 | { | 319 | { | ||
266 | ProfileManager::instance()->setDefaultProfile(currentProfile()); | 320 | ProfileManager::instance()->setDefaultProfile(currentProfile()); | ||
267 | // do not allow the new default session type to be removed | 321 | // do not allow the new default session type to be removed | ||
268 | deleteProfileButton->setEnabled(false); | 322 | deleteProfileButton->setEnabled(false); | ||
269 | setAsDefaultButton->setEnabled(false); | 323 | setAsDefaultButton->setEnabled(false); | ||
270 | 324 | | |||
271 | // update font of new default item | 325 | // update font of new default item | ||
272 | updateDefaultItem(); | 326 | updateDefaultItem(); | ||
273 | } | 327 | } | ||
274 | 328 | | |||
275 | void ProfileSettings::moveUpSelected() | | |||
276 | { | | |||
277 | Q_ASSERT(_sessionModel); | | |||
278 | | ||||
279 | const int rowIndex = sessionTable->currentIndex().row(); | | |||
280 | const QList<QStandardItem*>items = _sessionModel->takeRow(rowIndex); | | |||
281 | _sessionModel->insertRow(rowIndex - 1, items); | | |||
282 | sessionTable->selectRow(rowIndex - 1); | | |||
283 | } | | |||
284 | | ||||
285 | void ProfileSettings::moveDownSelected() | | |||
286 | { | | |||
287 | Q_ASSERT(_sessionModel); | | |||
288 | | ||||
289 | const int rowIndex = sessionTable->currentIndex().row(); | | |||
290 | const QList<QStandardItem*>items = _sessionModel->takeRow(rowIndex); | | |||
291 | _sessionModel->insertRow(rowIndex + 1, items); | | |||
292 | sessionTable->selectRow(rowIndex + 1); | | |||
293 | } | | |||
294 | | ||||
295 | void ProfileSettings::createProfile() | 329 | void ProfileSettings::createProfile() | ||
296 | { | 330 | { | ||
297 | // setup a temporary profile which is a clone of the selected profile | 331 | // setup a temporary profile which is a clone of the selected profile | ||
298 | // or the default if no profile is selected | 332 | // or the default if no profile is selected | ||
299 | Profile::Ptr sourceProfile = currentProfile() ? currentProfile() : ProfileManager::instance()->defaultProfile(); | 333 | Profile::Ptr sourceProfile = currentProfile() ? currentProfile() : ProfileManager::instance()->defaultProfile(); | ||
300 | 334 | | |||
301 | Q_ASSERT(sourceProfile); | 335 | Q_ASSERT(sourceProfile); | ||
302 | 336 | | |||
303 | auto newProfile = Profile::Ptr(new Profile(ProfileManager::instance()->fallbackProfile())); | 337 | auto newProfile = Profile::Ptr(new Profile(ProfileManager::instance()->fallbackProfile())); | ||
304 | newProfile->clone(sourceProfile, true); | 338 | newProfile->clone(sourceProfile, true); | ||
339 | // TODO: add number suffix when the name is taken | ||||
305 | newProfile->setProperty(Profile::Name, i18nc("@item This will be used as part of the file name", "New Profile")); | 340 | newProfile->setProperty(Profile::Name, i18nc("@item This will be used as part of the file name", "New Profile")); | ||
306 | newProfile->setProperty(Profile::UntranslatedName, QStringLiteral("New Profile")); | 341 | newProfile->setProperty(Profile::UntranslatedName, QStringLiteral("New Profile")); | ||
307 | newProfile->setProperty(Profile::MenuIndex, QStringLiteral("0")); | 342 | newProfile->setProperty(Profile::MenuIndex, QStringLiteral("0")); | ||
308 | 343 | | |||
309 | // Consider https://blogs.kde.org/2009/03/26/how-crash-almost-every-qtkde-application-and-how-fix-it-0 before changing the below | 344 | // Consider https://blogs.kde.org/2009/03/26/how-crash-almost-every-qtkde-application-and-how-fix-it-0 before changing the below | ||
310 | QPointer<EditProfileDialog> dialog = new EditProfileDialog(this); | 345 | QPointer<EditProfileDialog> dialog = new EditProfileDialog(this); | ||
311 | dialog.data()->setProfile(newProfile); | 346 | dialog.data()->setProfile(newProfile); | ||
312 | dialog.data()->selectProfileName(); | 347 | dialog.data()->selectProfileName(); | ||
Show All 33 Lines | 357 | { | |||
346 | group->updateValues(); | 381 | group->updateValues(); | ||
347 | 382 | | |||
348 | dialog.setProfile(Profile::Ptr(group)); | 383 | dialog.setProfile(Profile::Ptr(group)); | ||
349 | dialog.exec(); | 384 | dialog.exec(); | ||
350 | } | 385 | } | ||
351 | QList<Profile::Ptr> ProfileSettings::selectedProfiles() const | 386 | QList<Profile::Ptr> ProfileSettings::selectedProfiles() const | ||
352 | { | 387 | { | ||
353 | QList<Profile::Ptr> list; | 388 | QList<Profile::Ptr> list; | ||
354 | QItemSelectionModel* selection = sessionTable->selectionModel(); | 389 | QItemSelectionModel* selection = profilesList->selectionModel(); | ||
355 | if (selection == nullptr) { | 390 | if (selection == nullptr) { | ||
356 | return list; | 391 | return list; | ||
357 | } | 392 | } | ||
358 | 393 | | |||
359 | foreach(const QModelIndex & index, selection->selectedIndexes()) { | 394 | foreach(const QModelIndex & index, selection->selectedIndexes()) { | ||
360 | if (index.column() == ProfileNameColumn) { | 395 | if (index.column() == ProfileColumn) { | ||
361 | list << index.data(ProfileKeyRole).value<Profile::Ptr>(); | 396 | list << index.data(ProfilePtrRole).value<Profile::Ptr>(); | ||
362 | } | 397 | } | ||
363 | } | 398 | } | ||
364 | 399 | | |||
365 | return list; | 400 | return list; | ||
366 | } | 401 | } | ||
367 | Profile::Ptr ProfileSettings::currentProfile() const | 402 | Profile::Ptr ProfileSettings::currentProfile() const | ||
368 | { | 403 | { | ||
369 | QItemSelectionModel* selection = sessionTable->selectionModel(); | 404 | QItemSelectionModel* selection = profilesList->selectionModel(); | ||
370 | 405 | | |||
371 | if ((selection == nullptr) || selection->selectedRows().count() != 1) { | 406 | if ((selection == nullptr) || selection->selectedRows().count() != 1) { | ||
372 | return Profile::Ptr(); | 407 | return Profile::Ptr(); | ||
373 | } | 408 | } | ||
374 | 409 | | |||
375 | return selection-> | 410 | return selection-> | ||
376 | selectedIndexes().first().data(ProfileKeyRole).value<Profile::Ptr>(); | 411 | selectedIndexes().at(ProfileColumn).data(ProfilePtrRole).value<Profile::Ptr>(); | ||
377 | } | 412 | } | ||
378 | bool ProfileSettings::isProfileDeletable(Profile::Ptr profile) const | 413 | bool ProfileSettings::isProfileDeletable(Profile::Ptr profile) const | ||
379 | { | 414 | { | ||
380 | if (!profile) { | 415 | if (!profile) { | ||
381 | return false; | 416 | return false; | ||
382 | } | 417 | } | ||
383 | 418 | | |||
384 | const QFileInfo fileInfo(profile->path()); | 419 | const QFileInfo fileInfo(profile->path()); | ||
385 | if (!fileInfo.exists()) { | 420 | if (!fileInfo.exists()) { | ||
386 | return false; | 421 | return false; | ||
387 | } | 422 | } | ||
388 | 423 | | |||
389 | const QFileInfo dirInfo(fileInfo.path()); | 424 | const QFileInfo dirInfo(fileInfo.path()); | ||
390 | return dirInfo.isWritable(); | 425 | return dirInfo.isWritable(); | ||
391 | } | 426 | } | ||
392 | void ProfileSettings::updateFavoriteStatus(const Profile::Ptr &profile, bool favorite) | 427 | void ProfileSettings::updateFavoriteStatus(const Profile::Ptr &profile, bool favorite) | ||
393 | { | 428 | { | ||
394 | Q_ASSERT(_sessionModel); | 429 | Q_ASSERT(_sessionModel); | ||
395 | 430 | | |||
396 | const int rowCount = _sessionModel->rowCount(); | 431 | const int rowCount = _sessionModel->rowCount(); | ||
397 | for (int i = 0; i < rowCount; i++) { | 432 | for (int i = 0; i < rowCount; i++) { | ||
398 | QModelIndex index = _sessionModel->index(i, FavoriteStatusColumn); | 433 | auto *item = _sessionModel->item(i, ProfileColumn); | ||
399 | if (index.data(ProfileKeyRole).value<Profile::Ptr>() == profile) { | 434 | if (item->data(ProfilePtrRole).value<Profile::Ptr>() == profile) { | ||
400 | // FIXME: On desktops without this icon, it is impossible to | 435 | auto *favoriteItem = _sessionModel->item(i, FavoriteStatusColumn); | ||
401 | // determine if a profile is a favorite in this dialog. | 436 | favoriteItem->setCheckState(favorite ? Qt::Checked : Qt::Unchecked); | ||
402 | // Consider changing to using QStandardItem::setCheckable | 437 | break; | ||
403 | const QIcon icon = favorite ? QIcon::fromTheme(QStringLiteral("dialog-ok-apply")) : QIcon(); | | |||
404 | _sessionModel->setData(index, icon, Qt::DecorationRole); | | |||
405 | } | 438 | } | ||
406 | } | 439 | } | ||
407 | } | 440 | } | ||
408 | void ProfileSettings::setShortcutEditorVisible(bool visible) | 441 | void ProfileSettings::setShortcutEditorVisible(bool visible) | ||
409 | { | 442 | { | ||
410 | sessionTable->setColumnHidden(ShortcutColumn, !visible); | 443 | profilesList->setColumnHidden(ShortcutColumn, !visible); | ||
411 | } | 444 | } | ||
412 | void StyledBackgroundPainter::drawBackground(QPainter* painter, const QStyleOptionViewItem& option, | 445 | void StyledBackgroundPainter::drawBackground(QPainter* painter, const QStyleOptionViewItem& option, | ||
413 | const QModelIndex&) | 446 | const QModelIndex&) | ||
414 | { | 447 | { | ||
415 | const auto* opt = qstyleoption_cast<const QStyleOptionViewItem*>(&option); | 448 | const auto* opt = qstyleoption_cast<const QStyleOptionViewItem*>(&option); | ||
416 | const QWidget* widget = opt != nullptr ? opt->widget : nullptr; | 449 | const QWidget* widget = opt != nullptr ? opt->widget : nullptr; | ||
417 | 450 | | |||
418 | QStyle* style = widget != nullptr ? widget->style() : QApplication::style(); | 451 | QStyle* style = widget != nullptr ? widget->style() : QApplication::style(); | ||
419 | 452 | | |||
420 | style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, widget); | 453 | style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, widget); | ||
421 | } | 454 | } | ||
422 | 455 | | |||
423 | // This adds a checkmark and the appropriate background in the "Show" | | |||
424 | // column of the Manage Profiles->Profiles page. | | |||
425 | FavoriteItemDelegate::FavoriteItemDelegate(QObject* aParent) | | |||
426 | : QStyledItemDelegate(aParent) | | |||
427 | { | | |||
428 | } | | |||
429 | void FavoriteItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const | | |||
430 | { | | |||
431 | // See implementation of QStyledItemDelegate::paint() | | |||
432 | QStyleOptionViewItem opt = option; | | |||
433 | initStyleOption(&opt, index); | | |||
434 | | ||||
435 | StyledBackgroundPainter::drawBackground(painter, opt, index); | | |||
436 | | ||||
437 | int margin = (opt.rect.height() - opt.decorationSize.height()) / 2; | | |||
438 | margin++; | | |||
439 | | ||||
440 | opt.rect.setTop(opt.rect.top() + margin); | | |||
441 | opt.rect.setBottom(opt.rect.bottom() - margin); | | |||
442 | | ||||
443 | QIcon icon = index.data(Qt::DecorationRole).value<QIcon>(); | | |||
444 | icon.paint(painter, opt.rect, Qt::AlignCenter); | | |||
445 | } | | |||
446 | | ||||
447 | bool FavoriteItemDelegate::editorEvent(QEvent* aEvent, QAbstractItemModel*, | | |||
448 | const QStyleOptionViewItem&, const QModelIndex& index) | | |||
449 | { | | |||
450 | if (aEvent->type() == QEvent::MouseButtonPress || | | |||
451 | aEvent->type() == QEvent::KeyPress || | | |||
452 | aEvent->type() == QEvent::MouseButtonDblClick) { | | |||
453 | Profile::Ptr profile = index.data(ProfileSettings::ProfileKeyRole).value<Profile::Ptr>(); | | |||
454 | const bool isFavorite = ProfileManager::instance()->findFavorites().contains(profile); | | |||
455 | | ||||
456 | ProfileManager::instance()->setFavorite(profile, !isFavorite); | | |||
457 | } | | |||
458 | | ||||
459 | return true; | | |||
460 | } | | |||
461 | ShortcutItemDelegate::ShortcutItemDelegate(QObject* aParent) | 456 | ShortcutItemDelegate::ShortcutItemDelegate(QObject* aParent) | ||
462 | : QStyledItemDelegate(aParent), | 457 | : QStyledItemDelegate(aParent), | ||
463 | _modifiedEditors(QSet<QWidget *>()), | 458 | _modifiedEditors(QSet<QWidget *>()), | ||
464 | _itemsBeingEdited(QSet<QModelIndex>()) | 459 | _itemsBeingEdited(QSet<QModelIndex>()) | ||
465 | { | 460 | { | ||
466 | } | 461 | } | ||
467 | void ShortcutItemDelegate::editorModified(const QKeySequence& keys) | 462 | void ShortcutItemDelegate::editorModified() | ||
468 | { | 463 | { | ||
469 | Q_UNUSED(keys); | 464 | auto* editor = qobject_cast<FilteredKeySequenceEdit*>(sender()); | ||
470 | | ||||
471 | auto* editor = qobject_cast<KKeySequenceWidget*>(sender()); | | |||
472 | Q_ASSERT(editor); | 465 | Q_ASSERT(editor); | ||
473 | _modifiedEditors.insert(editor); | 466 | _modifiedEditors.insert(editor); | ||
474 | emit commitData(editor); | 467 | emit commitData(editor); | ||
475 | emit closeEditor(editor); | 468 | emit closeEditor(editor); | ||
476 | } | 469 | } | ||
477 | void ShortcutItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, | 470 | void ShortcutItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, | ||
478 | const QModelIndex& index) const | 471 | const QModelIndex& index) const | ||
479 | { | 472 | { | ||
480 | _itemsBeingEdited.remove(index); | 473 | _itemsBeingEdited.remove(index); | ||
481 | 474 | | |||
482 | if (!_modifiedEditors.contains(editor)) { | 475 | if (!_modifiedEditors.contains(editor)) { | ||
483 | return; | 476 | return; | ||
484 | } | 477 | } | ||
485 | 478 | | |||
486 | QString shortcut = qobject_cast<KKeySequenceWidget*>(editor)->keySequence().toString(); | 479 | QString shortcut = qobject_cast<FilteredKeySequenceEdit *>(editor)->keySequence().toString(); | ||
487 | model->setData(index, shortcut, Qt::DisplayRole); | 480 | model->setData(index, shortcut, Qt::DisplayRole); | ||
488 | 481 | | |||
489 | _modifiedEditors.remove(editor); | 482 | _modifiedEditors.remove(editor); | ||
490 | } | 483 | } | ||
491 | 484 | | |||
492 | QWidget* ShortcutItemDelegate::createEditor(QWidget* aParent, const QStyleOptionViewItem&, const QModelIndex& index) const | 485 | QWidget* ShortcutItemDelegate::createEditor(QWidget* aParent, const QStyleOptionViewItem&, const QModelIndex& index) const | ||
493 | { | 486 | { | ||
494 | _itemsBeingEdited.insert(index); | 487 | _itemsBeingEdited.insert(index); | ||
495 | 488 | | |||
496 | auto editor = new KKeySequenceWidget(aParent); | 489 | auto editor = new FilteredKeySequenceEdit(aParent); | ||
497 | editor->setFocusPolicy(Qt::StrongFocus); | | |||
498 | editor->setModifierlessAllowed(false); | | |||
499 | QString shortcutString = index.data(Qt::DisplayRole).toString(); | 490 | QString shortcutString = index.data(Qt::DisplayRole).toString(); | ||
500 | editor->setKeySequence(QKeySequence::fromString(shortcutString)); | 491 | editor->setKeySequence(QKeySequence::fromString(shortcutString)); | ||
501 | connect(editor, &KKeySequenceWidget::keySequenceChanged, this, &Konsole::ShortcutItemDelegate::editorModified); | 492 | connect(editor, &QKeySequenceEdit::editingFinished, this, &Konsole::ShortcutItemDelegate::editorModified); | ||
502 | editor->captureKeySequence(); | 493 | editor->setFocus(Qt::FocusReason::MouseFocusReason); | ||
503 | return editor; | 494 | return editor; | ||
504 | } | 495 | } | ||
505 | void ShortcutItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, | 496 | void ShortcutItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, | ||
506 | const QModelIndex& index) const | 497 | const QModelIndex& index) const | ||
507 | { | 498 | { | ||
508 | if (_itemsBeingEdited.contains(index)) { | 499 | if (_itemsBeingEdited.contains(index)) { | ||
509 | StyledBackgroundPainter::drawBackground(painter, option, index); | 500 | StyledBackgroundPainter::drawBackground(painter, option, index); | ||
510 | } else { | 501 | } else { | ||
511 | QStyledItemDelegate::paint(painter, option, index); | 502 | QStyledItemDelegate::paint(painter, option, index); | ||
512 | } | 503 | } | ||
513 | } | 504 | } | ||
514 | 505 | | |||
506 | QSize Konsole::ShortcutItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const | ||||
507 | { | ||||
508 | const QString shortcutString = index.data(Qt::DisplayRole).toString(); | ||||
509 | QFontMetrics fm = option.fontMetrics; | ||||
510 | | ||||
511 | static const int editorMargins = 16; // chosen empirically | ||||
512 | const int width = fm.horizontalAdvance(shortcutString + QStringLiteral(", ...")) | ||||
513 | + editorMargins; | ||||
514 | | ||||
515 | return QSize(width, QStyledItemDelegate::sizeHint(option, index).height()); | ||||
516 | } | ||||
517 | | ||||
518 | void Konsole::ShortcutItemDelegate::destroyEditor(QWidget *editor, const QModelIndex &index) const | ||||
519 | { | ||||
520 | _itemsBeingEdited.remove(index); | ||||
521 | _modifiedEditors.remove(editor); | ||||
522 | editor->deleteLater(); | ||||
523 | } | ||||
524 | | ||||
525 | void Konsole::FilteredKeySequenceEdit::keyPressEvent(QKeyEvent *event) | ||||
526 | { | ||||
527 | if(event->modifiers() == Qt::NoModifier) { | ||||
528 | switch(event->key()) { | ||||
529 | case Qt::Key_Enter: | ||||
530 | case Qt::Key_Return: | ||||
531 | emit editingFinished(); | ||||
532 | return; | ||||
533 | case Qt::Key_Backspace: | ||||
534 | case Qt::Key_Delete: | ||||
535 | clear(); | ||||
536 | emit editingFinished(); | ||||
537 | event->accept(); | ||||
538 | return; | ||||
539 | default: | ||||
540 | event->accept(); | ||||
541 | return; | ||||
542 | } | ||||
543 | } | ||||
544 | QKeySequenceEdit::keyPressEvent(event); | ||||
545 | } |