swift
settingshotkeycomponent.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2015 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QAbstractItemModel>
7 #include <QFlags>
8 #include <QItemSelectionModel>
9 #include <QList>
10 #include <QMessageBox>
11 #include <QModelIndex>
12 #include <QModelIndexList>
13 #include <QPushButton>
14 #include <QString>
15 #include <QTableView>
16 #include <QVariant>
17 #include <QtGlobal>
18 
19 #include "ui_settingshotkeycomponent.h"
20 
23 #include "core/inputmanager.h"
26 #include "gui/guiapplication.h"
28 #include "misc/metadatautils.h"
29 
30 using namespace swift::misc;
31 using namespace swift::misc::input;
32 using namespace swift::gui::models;
33 using namespace swift::core;
34 using namespace swift::core::context;
35 
36 namespace swift::gui::components
37 {
38  CSettingsHotkeyComponent::CSettingsHotkeyComponent(QWidget *parent)
39  : QFrame(parent), ui(new Ui::CSettingsHotkeyComponent)
40  {
41  Q_ASSERT_X(sGui, Q_FUNC_INFO, "Missing sGui");
42  ui->setupUi(this);
43  ui->tv_Hotkeys->setModel(&m_model);
44 
45  connect(ui->pb_AddHotkey, &QPushButton::clicked, this, &CSettingsHotkeyComponent::addEntry);
46  connect(ui->pb_EditHotkey, &QPushButton::clicked, this, &CSettingsHotkeyComponent::editEntry);
47  connect(ui->pb_RemoveHotkey, &QPushButton::clicked, this, &CSettingsHotkeyComponent::removeEntry);
49 
51  ui->tv_Hotkeys->selectRow(0);
52  }
53 
55 
57  {
58  const CStatusMessage msg = m_actionHotkeys.save();
59  CLogMessage::preformatted(msg);
60  }
61 
63  {
64  Q_ASSERT_X(sApp && sApp->getInputManager(), Q_FUNC_INFO, "Missing input manager");
65  sApp->getInputManager()->registerAction(pttHotkeyAction(), pttHotkeyIcon());
66  }
67 
68  void CSettingsHotkeyComponent::addEntry()
69  {
70  const CActionHotkey selectedActionHotkey =
71  CHotkeyDialog::getActionHotkey(CActionHotkey(), getAllIdentifiers(), this);
72  if (selectedActionHotkey.isValid() && checkAndConfirmConflicts(selectedActionHotkey))
73  {
74  addHotkeyToSettings(selectedActionHotkey);
75  const int position = m_model.rowCount();
76  m_model.insertRows(position, 1, QModelIndex());
77  const QModelIndex index = m_model.index(position, 0, QModelIndex());
78  m_model.setData(index, QVariant::fromValue(selectedActionHotkey), CActionHotkeyListModel::ActionHotkeyRole);
79 
80  // T784, further info about the "key"/button
81  CLogMessage(this).debug(u"%1, added key: '%2'")
82  << classNameShort(this) << selectedActionHotkey.toQString(true);
83  }
84  this->resizeView();
85  }
86 
87  void CSettingsHotkeyComponent::editEntry()
88  {
89  const auto index = ui->tv_Hotkeys->selectionModel()->currentIndex();
90  if (!index.isValid()) { return; }
91 
92  const auto model = ui->tv_Hotkeys->model();
93  const QModelIndex indexHotkey = model->index(index.row(), 0, QModelIndex());
94  Q_ASSERT_X(indexHotkey.data(CActionHotkeyListModel::ActionHotkeyRole).canConvert<CActionHotkey>(), Q_FUNC_INFO,
95  "No action hotkey");
96  const auto actionHotkey = indexHotkey.data(CActionHotkeyListModel::ActionHotkeyRole).value<CActionHotkey>();
97  const CActionHotkey selectedActionHotkey =
98  CHotkeyDialog::getActionHotkey(actionHotkey, getAllIdentifiers(), this);
99  if (selectedActionHotkey.isValid() && checkAndConfirmConflicts(selectedActionHotkey, { actionHotkey }))
100  {
101  updateHotkeyInSettings(actionHotkey, selectedActionHotkey);
102  m_model.setData(indexHotkey, QVariant::fromValue(selectedActionHotkey),
103  CActionHotkeyListModel::ActionHotkeyRole);
104 
105  // T784, further info about the "key"/button
106  CLogMessage(this).debug(u"%1, edited key: '%2'")
107  << classNameShort(this) << selectedActionHotkey.toQString(true);
108  }
109  this->resizeView();
110  }
111 
112  void CSettingsHotkeyComponent::removeEntry()
113  {
114  const QModelIndexList indexes = ui->tv_Hotkeys->selectionModel()->selectedRows();
115  for (const auto &index : indexes)
116  {
117  const auto actionHotkey = index.data(CActionHotkeyListModel::ActionHotkeyRole).value<CActionHotkey>();
118  removeHotkeyFromSettings(actionHotkey);
119  m_model.removeRows(index.row(), 1, QModelIndex());
120  }
121  this->resizeView();
122  }
123 
124  void CSettingsHotkeyComponent::addHotkeyToSettings(const CActionHotkey &actionHotkey)
125  {
126  CActionHotkeyList actionHotkeyList(m_actionHotkeys.getThreadLocal());
127  actionHotkeyList.push_back(actionHotkey);
128  m_actionHotkeys.set(actionHotkeyList);
129  }
130 
131  void CSettingsHotkeyComponent::updateHotkeyInSettings(const CActionHotkey &oldValue, const CActionHotkey &newValue)
132  {
134  this->removeHotkeyFromSettings(oldValue);
135  this->addHotkeyToSettings(newValue);
136 
137  // CActionHotkeyList actionHotkeyList(m_actionHotkeys.getThreadLocal());
138  // actionHotkeyList.replace(oldValue, newValue);
139  // m_actionHotkeys.set(actionHotkeyList);
140  }
141 
142  void CSettingsHotkeyComponent::removeHotkeyFromSettings(const CActionHotkey &actionHotkey)
143  {
144  CActionHotkeyList actionHotkeyList(m_actionHotkeys.getThreadLocal());
145  actionHotkeyList.remove(actionHotkey);
146  m_actionHotkeys.set(actionHotkeyList);
147  }
148 
149  bool CSettingsHotkeyComponent::checkAndConfirmConflicts(const CActionHotkey &actionHotkey,
150  const CActionHotkeyList &ignore)
151  {
152  // check the hotkeys of the same machine only
153  // and avoid duplicates (replace or add)
154  const CActionHotkeyList configuredHotkeysSameMachine =
155  m_actionHotkeys.getThreadLocal().findBySameMachine(actionHotkey);
156  CActionHotkeyList conflicts = configuredHotkeysSameMachine.findSupersetsOf(actionHotkey);
157  conflicts.replaceOrAdd(configuredHotkeysSameMachine.findSubsetsOf(actionHotkey));
158  conflicts.removeIfIn(ignore.findBySameMachine(actionHotkey));
159 
160  if (!conflicts.isEmpty())
161  {
162  QString message =
163  QStringLiteral("The selected combination conflicts with the following %1 combination(s):\n\n")
164  .arg(conflicts.size());
165  for (const CActionHotkey &conflict : conflicts)
166  {
167  message += conflict.toQString();
168  message += "\n";
169  }
170  message += "\n Do you want to use it anway?";
171  const auto reply = QMessageBox::warning(this, "SettingsHotkeyComponent", message,
173  if (reply == QMessageBox::No) { return false; }
174  }
175  return true;
176  }
177 
179  {
180  const CActionHotkeyList hotkeys = m_actionHotkeys.getThreadLocal();
181  m_model.clear();
182 
183  // list of all defined hotkeys (not the dialog)
184  for (const CActionHotkey &hotkey : hotkeys)
185  {
186  const int position = m_model.rowCount();
187  m_model.insertRows(position, 1, QModelIndex());
188  const QModelIndex index = m_model.index(position, 0, QModelIndex());
189  m_model.setData(index, QVariant::fromValue(hotkey), CActionHotkeyListModel::ActionHotkeyRole);
190  }
191  this->resizeView();
192  }
193 
194  CIdentifierList CSettingsHotkeyComponent::getAllIdentifiers() const
195  {
196  CIdentifierList identifiers;
197  if (!sGui || !sGui->getIContextApplication()) { return identifiers; }
199  {
201  }
202 
203  // add local application
204  identifiers.push_back(CIdentifier("local identifer for hotkeys"));
205  return identifiers;
206  }
207 
208  void CSettingsHotkeyComponent::resizeView() { ui->tv_Hotkeys->resizeRowsToContents(); }
209 
210  void CSettingsHotkeyComponent::hotkeySlot(bool keyDown)
211  {
212  if (keyDown)
213  {
214  auto *msgBox = new QMessageBox(this);
215  msgBox->setAttribute(Qt::WA_DeleteOnClose);
216  msgBox->setStandardButtons(QMessageBox::Ok);
217  msgBox->setWindowTitle("Test");
218  msgBox->setText("Hotkey test");
219  msgBox->setIcon(QMessageBox::Information);
220  msgBox->setModal(false);
221  msgBox->open();
222  }
223  }
224 
226  {
227  Q_ASSERT_X(m_config, Q_FUNC_INFO, "Missing configuration");
228  if (CConfigurationWizard::lastWizardStepSkipped(this->wizard())) { return true; }
229  m_config->saveSettings();
230  return true;
231  }
232 
234  {
235  Q_ASSERT_X(m_config, Q_FUNC_INFO, "Missing configuration");
236  m_config->reloadHotkeysFromSettings();
237  }
238 } // namespace swift::gui::components
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
Definition: application.cpp:71
CInputManager * getInputManager() const
The input manager, if available.
Definition: application.h:302
const context::IContextApplication * getIContextApplication() const
Direct access to contexts if a CCoreFacade has been initialized.
void registerAction(const QString &action, swift::misc::CIcons::IconIndex icon=swift::misc::CIcons::StandardIconEmpty16)
Register new action.
virtual misc::CIdentifierList getRegisteredApplications() const =0
All registered applications.
static bool lastWizardStepSkipped(const QWizard *standardWizard)
Static version of CConfigurationWizard::lastStepSkipped.
static swift::misc::input::CActionHotkey getActionHotkey(const swift::misc::input::CActionHotkey &initial, const swift::misc::CIdentifierList &identifiers, QWidget *parent=nullptr)
Runs the hotkey dialog and returns the result.
void registerDummyPttEntry()
Create dummy/emtpy Ptt entry for wizard.
bool removeRows(int position, int rows, const QModelIndex &index)
int rowCount(const QModelIndex &parent=QModelIndex()) const
bool setData(const QModelIndex &index, const QVariant &var, int role)
bool insertRows(int position, int rows, const QModelIndex &index)
CStatusMessage save()
Save using the currently set value. Must be called from the thread in which the owner lives.
Definition: valuecache.h:423
CStatusMessage set(const T &value, qint64 timestamp=0)
Write a new value. Must be called from the thread in which the owner lives.
Definition: valuecache.h:411
const T & getThreadLocal() const
Read the current value.
Definition: valuecache.h:400
Value object encapsulating information identifying a component of a modular distributed swift process...
Definition: identifier.h:29
Value object encapsulating a list of object identifiers.
Class for emitting a log message.
Definition: logmessage.h:27
Derived & debug()
Set the severity to debug.
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
void replaceOrAdd(const T &original, const T &replacement)
Replace elements matching the given element. If there is no match, push the new element on the end.
Definition: sequence.h:521
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
int removeIfIn(const CSequence &other)
Remove all elements if they are in other.
Definition: sequence.h:464
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
Streamable status message, e.g.
Value object encapsulating a action hotkey.
Definition: actionhotkey.h:25
bool isValid() const
Is hotkey valid?
Definition: actionhotkey.h:85
Value object encapsulating a list of hotkeys.
CActionHotkeyList findBySameMachine(const CActionHotkey &key) const
Find hotkeys for the same machine.
CActionHotkeyList findSupersetsOf(const CActionHotkey &other) const
Returns true if this list has a hotkey with a combination for which other is a superset of other Exam...
CActionHotkeyList findSubsetsOf(const CActionHotkey &other) const
Returns true if this list has a action hotkey with a combination which is a subset of other Example: ...
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:74
SWIFT_GUI_EXPORT swift::gui::CGuiApplication * sGui
Single instance of GUI application object.
Backend services of the swift project, like dealing with the network or the simulators.
Definition: actionbind.cpp:7
High level reusable GUI components.
Definition: aboutdialog.cpp:14
Models to be used with views, mainly QTableView.
Free functions in swift::misc.
QString classNameShort(const QObject *object)
Class name as from QMetaObject::className without namespace.
void clicked(bool checked)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
QMessageBox::StandardButton warning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
QVariant data(int role) const const
bool isValid() const const
int row() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString arg(Args &&... args) const const
WA_DeleteOnClose
bool canConvert() const const
QVariant fromValue(T &&value)
T value() const &const
QWizard * wizard() const const