1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. |
2 | // |
3 | // SPDX-License-Identifier: GPL-3.0-or-later |
4 | |
5 | #include "shortcutsettingwidget.h" |
6 | #include "common/common.h" |
7 | |
8 | #include <QtWidgets/QTableView> |
9 | #include <QtWidgets/QLabel> |
10 | #include <QtWidgets/QLineEdit> |
11 | #include <QtWidgets/QPushButton> |
12 | #include <QHeaderView> |
13 | #include <QtWidgets/QVBoxLayout> |
14 | #include <QtWidgets/QHBoxLayout> |
15 | #include <QDebug> |
16 | #include <QDir> |
17 | #include <QFileDialog> |
18 | |
19 | #define BTN_WIDTH (180) |
20 | |
21 | class ShortcutTableModelPrivate |
22 | { |
23 | QMap<QString, QStringList> shortcutItemMap; |
24 | QMap<QString, QStringList> shortcutItemShadowMap; |
25 | QString configFilePath; |
26 | |
27 | friend class ShortcutTableModel; |
28 | }; |
29 | |
30 | ShortcutTableModel::ShortcutTableModel(QObject *parent) |
31 | : QAbstractTableModel(parent) |
32 | , d(new ShortcutTableModelPrivate()) |
33 | { |
34 | d->configFilePath = (CustomPaths::user(CustomPaths::Flags::Configures) |
35 | + QDir::separator() + QString("shortcut.support" )); |
36 | } |
37 | |
38 | ShortcutTableModel::~ShortcutTableModel() |
39 | { |
40 | |
41 | } |
42 | |
43 | int ShortcutTableModel::rowCount(const QModelIndex &parent) const |
44 | { |
45 | Q_UNUSED(parent) |
46 | |
47 | return d->shortcutItemMap.size(); |
48 | } |
49 | |
50 | int ShortcutTableModel::columnCount(const QModelIndex &parent) const |
51 | { |
52 | Q_UNUSED(parent) |
53 | |
54 | return ColumnID::_KCount; |
55 | } |
56 | |
57 | |
58 | bool ShortcutTableModel::keySequenceIsInvalid(const QKeySequence &sequence) const |
59 | { |
60 | for (uint i = 0; i < static_cast<uint>(sequence.count()); i++) { |
61 | if (Qt::Key_unknown == sequence[i]) { |
62 | return true; |
63 | } |
64 | } |
65 | |
66 | return false; |
67 | } |
68 | |
69 | bool ShortcutTableModel::shortcutRepeat(const QString &text) const |
70 | { |
71 | int count = 0; |
72 | foreach (QString key, d->shortcutItemMap.keys()) { |
73 | QStringList valueList = d->shortcutItemMap.value(key); |
74 | if (0 == text.compare(valueList.last(), Qt::CaseInsensitive)) { |
75 | if (count++ > 0) |
76 | return true; |
77 | } |
78 | } |
79 | |
80 | return false; |
81 | } |
82 | |
83 | QVariant ShortcutTableModel::data(const QModelIndex &index, int role) const |
84 | { |
85 | if (!index.isValid()) |
86 | return QVariant(); |
87 | |
88 | if (role != Qt::DisplayRole && role != Qt::ForegroundRole) |
89 | return QVariant(); |
90 | |
91 | if (index.row() >= d->shortcutItemMap.keys().size()) |
92 | return QVariant(); |
93 | |
94 | QString id = d->shortcutItemMap.keys()[index.row()]; |
95 | QStringList valueList = d->shortcutItemMap.value(id); |
96 | |
97 | QString description = valueList.first(); |
98 | QString shortcut = valueList.last(); |
99 | |
100 | if(role == Qt::ForegroundRole && index.column() == ColumnID::kShortcut) { |
101 | if(shortcutRepeat(shortcut) || keySequenceIsInvalid(QKeySequence(shortcut))) { |
102 | return QColor(Qt::darkRed); |
103 | } |
104 | return QVariant(); |
105 | } |
106 | |
107 | switch (index.column()) { |
108 | case ColumnID::kID: |
109 | return id; |
110 | case ColumnID::kDescriptions: |
111 | return description; |
112 | case ColumnID::kShortcut: |
113 | return shortcut; |
114 | default: |
115 | return QVariant(); |
116 | } |
117 | } |
118 | |
119 | QVariant ShortcutTableModel::(int section, Qt::Orientation orientation, int role) const |
120 | { |
121 | if (orientation == Qt::Vertical) |
122 | return QVariant(); |
123 | |
124 | if (role != Qt::DisplayRole) |
125 | return QVariant(); |
126 | |
127 | switch (section) { |
128 | case ColumnID::kID: |
129 | return tr("ID" ); |
130 | case ColumnID::kDescriptions: |
131 | return tr("Description" ); |
132 | case ColumnID::kShortcut: |
133 | return tr("Shortcut" ); |
134 | default: |
135 | return QVariant(); |
136 | } |
137 | } |
138 | |
139 | void ShortcutTableModel::updateShortcut(QString id, QString shortcut) |
140 | { |
141 | if (d->shortcutItemMap.keys().contains(id)) |
142 | { |
143 | QStringList valueList = d->shortcutItemMap.value(id); |
144 | QStringList newValueList = {valueList.first(), shortcut}; |
145 | d->shortcutItemMap[id] = newValueList; |
146 | } |
147 | } |
148 | |
149 | void ShortcutTableModel::resetShortcut(QString id) |
150 | { |
151 | if (d->shortcutItemMap.keys().contains(id) && d->shortcutItemShadowMap.keys().contains(id)) |
152 | { |
153 | QStringList shadowValueList = d->shortcutItemShadowMap.value(id); |
154 | d->shortcutItemMap[id] = shadowValueList; |
155 | } |
156 | } |
157 | |
158 | void ShortcutTableModel::resetAllShortcut() |
159 | { |
160 | d->shortcutItemMap = d->shortcutItemShadowMap; |
161 | } |
162 | |
163 | void ShortcutTableModel::saveShortcut() |
164 | { |
165 | ShortcutUtil::writeToJson(d->configFilePath, d->shortcutItemMap); |
166 | |
167 | QList<Command *> commandsList = ActionManager::getInstance()->commands(); |
168 | QList<Command *>::iterator iter = commandsList.begin(); |
169 | for (; iter != commandsList.end(); ++iter) |
170 | { |
171 | Action * action = dynamic_cast<Action *>(*iter); |
172 | QString id = action->id(); |
173 | |
174 | if (d->shortcutItemMap.contains(id)) { |
175 | QStringList valueList = d->shortcutItemMap[id]; |
176 | action->setKeySequence(QKeySequence(valueList.last())); |
177 | } |
178 | } |
179 | } |
180 | |
181 | void ShortcutTableModel::readShortcut() |
182 | { |
183 | beginResetModel(); |
184 | |
185 | QList<Command *> commandsList = ActionManager::getInstance()->commands(); |
186 | QList<Command *>::iterator iter = commandsList.begin(); |
187 | for (; iter != commandsList.end(); ++iter) |
188 | { |
189 | Action * action = dynamic_cast<Action *>(*iter); |
190 | QString id = action->id(); |
191 | QStringList valueList = QStringList{action->description(), action->keySequence().toString()}; |
192 | d->shortcutItemMap[id] = valueList; |
193 | } |
194 | |
195 | QMap<QString, QStringList> shortcutItemMap; |
196 | ShortcutUtil::readFromJson(d->configFilePath, shortcutItemMap); |
197 | foreach (const QString key, shortcutItemMap.keys()) { |
198 | d->shortcutItemMap[key] = shortcutItemMap.value(key); |
199 | } |
200 | |
201 | d->shortcutItemShadowMap = d->shortcutItemMap; |
202 | |
203 | endResetModel(); |
204 | } |
205 | |
206 | void ShortcutTableModel::importExternalJson(const QString &filePath) |
207 | { |
208 | beginResetModel(); |
209 | |
210 | QMap<QString, QStringList> shortcutItemMap; |
211 | ShortcutUtil::readFromJson(filePath, shortcutItemMap); |
212 | foreach (QString key, shortcutItemMap.keys()) { |
213 | d->shortcutItemMap[key] = shortcutItemMap.value(key); |
214 | } |
215 | |
216 | d->shortcutItemShadowMap = d->shortcutItemMap; |
217 | |
218 | endResetModel(); |
219 | } |
220 | |
221 | void ShortcutTableModel::exportExternalJson(const QString &filePath) |
222 | { |
223 | ShortcutUtil::writeToJson(filePath, d->shortcutItemMap); |
224 | } |
225 | |
226 | class ShortcutTableModel; |
227 | class ShortcutSettingWidgetPrivate |
228 | { |
229 | ShortcutSettingWidgetPrivate(); |
230 | QTableView *tableView; |
231 | HotkeyLineEdit *editShortCut; |
232 | ShortcutTableModel *model; |
233 | QPushButton *btnRecord; |
234 | QLabel *tipLabel; |
235 | |
236 | friend class ShortcutSettingWidget; |
237 | }; |
238 | |
239 | ShortcutSettingWidgetPrivate::ShortcutSettingWidgetPrivate() |
240 | : tableView(nullptr) |
241 | , editShortCut(nullptr) |
242 | , model(nullptr) |
243 | , btnRecord(nullptr) |
244 | , tipLabel(nullptr) |
245 | { |
246 | |
247 | } |
248 | |
249 | ShortcutSettingWidget::ShortcutSettingWidget(QWidget *parent) |
250 | : PageWidget(parent) |
251 | , d(new ShortcutSettingWidgetPrivate()) |
252 | { |
253 | setupUi(); |
254 | readConfig(); |
255 | } |
256 | |
257 | ShortcutSettingWidget::~ShortcutSettingWidget() |
258 | { |
259 | |
260 | } |
261 | |
262 | void ShortcutSettingWidget::setupUi() |
263 | { |
264 | QVBoxLayout *vLayout = new QVBoxLayout(); |
265 | setLayout(vLayout); |
266 | |
267 | d->tableView = new QTableView(); |
268 | d->tableView->setShowGrid(false); |
269 | d->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); |
270 | d->tableView->verticalHeader()->hide(); |
271 | d->tableView->setSelectionMode(QAbstractItemView::SingleSelection); |
272 | d->tableView->setSelectionBehavior(QAbstractItemView::SelectRows); |
273 | d->model = new ShortcutTableModel(); |
274 | d->tableView->setModel(d->model); |
275 | vLayout->addWidget(d->tableView); |
276 | |
277 | QWidget *widgetOperate = new QWidget(); |
278 | vLayout->addWidget(widgetOperate); |
279 | QHBoxLayout *hLayoutOperate = new QHBoxLayout(); |
280 | widgetOperate->setLayout(hLayoutOperate); |
281 | QPushButton *btnResetAll = new QPushButton(); |
282 | btnResetAll->setText(tr("Reset All" )); |
283 | btnResetAll->setFixedWidth(BTN_WIDTH); |
284 | QPushButton *btnImport = new QPushButton(); |
285 | btnImport->setText(tr("Import" )); |
286 | btnImport->setFixedWidth(BTN_WIDTH); |
287 | QPushButton *btnExport = new QPushButton(); |
288 | btnExport->setText(tr("Export" )); |
289 | btnExport->setFixedWidth(BTN_WIDTH); |
290 | hLayoutOperate->addWidget(btnResetAll); |
291 | hLayoutOperate->addStretch(); |
292 | hLayoutOperate->addWidget(btnImport); |
293 | hLayoutOperate->addWidget(btnExport); |
294 | |
295 | QWidget *widgetShortcut = new QWidget(); |
296 | vLayout->addWidget(widgetShortcut); |
297 | QHBoxLayout *hLayoutShortcut = new QHBoxLayout(); |
298 | widgetShortcut->setLayout(hLayoutShortcut); |
299 | QLabel *labelTip = new QLabel(tr("Shortcut:" )); |
300 | d->editShortCut = new HotkeyLineEdit(); |
301 | d->btnRecord = new QPushButton(tr("Record" )); |
302 | d->btnRecord->setFixedWidth(BTN_WIDTH); |
303 | QPushButton *btnReset = new QPushButton(tr("Reset" )); |
304 | btnReset->setFixedWidth(BTN_WIDTH); |
305 | hLayoutShortcut->addWidget(labelTip); |
306 | hLayoutShortcut->addWidget(d->editShortCut); |
307 | hLayoutShortcut->addWidget(d->btnRecord); |
308 | hLayoutShortcut->addWidget(btnReset); |
309 | |
310 | d->tipLabel = new QLabel(); |
311 | d->tipLabel->setMargin(10); |
312 | d->tipLabel->setStyleSheet("color:darkred;" ); |
313 | vLayout->addWidget(d->tipLabel); |
314 | |
315 | connect(d->tableView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(onTableViewClicked(const QModelIndex &))); |
316 | connect(btnResetAll, SIGNAL(clicked()), this, SLOT(onBtnResetAllClicked())); |
317 | connect(d->editShortCut, SIGNAL(textChanged(const QString &)), this, SLOT(onShortcutEditTextChanged(const QString &))); |
318 | connect(btnImport, SIGNAL(clicked()), this, SLOT(onBtnImportClicked())); |
319 | connect(btnExport, SIGNAL(clicked()), this, SLOT(onBtnExportClicked())); |
320 | connect(btnReset, SIGNAL(clicked()), this, SLOT(onBtnResetClicked())); |
321 | connect(d->btnRecord, SIGNAL(clicked()), this, SLOT(onBtnRecordClicked())); |
322 | } |
323 | |
324 | void ShortcutSettingWidget::setSelectedShortcut() |
325 | { |
326 | int row = d->tableView->currentIndex().row(); |
327 | QModelIndex index = d->model->index(row, ColumnID::kShortcut); |
328 | QString shortcut = d->model->data(index, Qt::DisplayRole).toString(); |
329 | d->editShortCut->setText(shortcut); |
330 | |
331 | checkShortcutValidity(row, shortcut); |
332 | } |
333 | |
334 | bool ShortcutSettingWidget::shortcutIsRepeat(const int row, const QString &text) |
335 | { |
336 | for (int i = 0; i < d->model->rowCount(); i++) { |
337 | if (row == i) |
338 | continue; |
339 | QModelIndex index = d->model->index(i, ColumnID::kShortcut); |
340 | QString shortcut = d->model->data(index, Qt::DisplayRole).toString(); |
341 | if (text == shortcut) { |
342 | return true; |
343 | } |
344 | } |
345 | |
346 | return false; |
347 | } |
348 | |
349 | void ShortcutSettingWidget::checkShortcutValidity(const int row, const QString &shortcut) |
350 | { |
351 | if (d->model->keySequenceIsInvalid(QKeySequence(shortcut))) { |
352 | d->tipLabel->setText(tr("Invalid shortcut!" )); |
353 | } else if (shortcutIsRepeat(row, shortcut)){ |
354 | d->tipLabel->setText(tr("shortcut Repeated!" )); |
355 | } else { |
356 | d->tipLabel->setText("" ); |
357 | } |
358 | } |
359 | |
360 | void ShortcutSettingWidget::onTableViewClicked(const QModelIndex &) |
361 | { |
362 | setSelectedShortcut(); |
363 | } |
364 | |
365 | void ShortcutSettingWidget::onBtnResetAllClicked() |
366 | { |
367 | d->model->resetAllShortcut(); |
368 | d->tableView->update(); |
369 | } |
370 | |
371 | void ShortcutSettingWidget::onBtnResetClicked() |
372 | { |
373 | int row = d->tableView->currentIndex().row(); |
374 | QModelIndex indexID = d->model->index(row, ColumnID::kID); |
375 | QString qsID = d->model->data(indexID, Qt::DisplayRole).toString(); |
376 | d->model->resetShortcut(qsID); |
377 | |
378 | setSelectedShortcut(); |
379 | } |
380 | |
381 | void ShortcutSettingWidget::onShortcutEditTextChanged(const QString &text) |
382 | { |
383 | QString shortcut = text.trimmed(); |
384 | int row = d->tableView->currentIndex().row(); |
385 | QModelIndex indexID = d->model->index(row, ColumnID::kID); |
386 | QString qsID = d->model->data(indexID, Qt::DisplayRole).toString(); |
387 | d->model->updateShortcut(qsID, shortcut); |
388 | |
389 | QModelIndex indexShortcut = d->model->index(row, ColumnID::kShortcut); |
390 | d->tableView->update(indexShortcut); |
391 | |
392 | checkShortcutValidity(row, shortcut); |
393 | } |
394 | |
395 | void ShortcutSettingWidget::saveConfig() |
396 | { |
397 | d->model->saveShortcut(); |
398 | } |
399 | |
400 | void ShortcutSettingWidget::readConfig() |
401 | { |
402 | d->model->readShortcut(); |
403 | } |
404 | |
405 | void ShortcutSettingWidget::onBtnImportClicked() |
406 | { |
407 | QString fileName = QFileDialog::getOpenFileName(this, tr("Open File" ), tr("" ), tr("Json File(*.json)" )); |
408 | if (!fileName.isEmpty()) { |
409 | d->model->importExternalJson(fileName); |
410 | } |
411 | } |
412 | |
413 | void ShortcutSettingWidget::onBtnExportClicked() |
414 | { |
415 | QString fileName = QFileDialog::getSaveFileName(this, tr("Save File" ), tr("" ), tr("Json File(*.json)" )); |
416 | if (!fileName.isEmpty()) { |
417 | d->model->exportExternalJson(fileName); |
418 | } |
419 | } |
420 | |
421 | void ShortcutSettingWidget::onBtnRecordClicked() |
422 | { |
423 | bool bRet = d->editShortCut->isHotkeyMode(); |
424 | if (bRet) { |
425 | d->btnRecord->setText(tr("Record" )); |
426 | d->editShortCut->setHotkeyMode(false); |
427 | } else { |
428 | d->btnRecord->setText(tr("Stop Recording" )); |
429 | d->editShortCut->setHotkeyMode(true); |
430 | } |
431 | } |
432 | |