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);
48  connect(ui->tb_ReloadHotkey, &QPushButton::clicked, this, &CSettingsHotkeyComponent::reloadHotkeysFromSettings);
49 
51  ui->tv_Hotkeys->selectRow(0);
52  }
53 
55 
57  {
58  const CStatusMessage msg = m_actionHotkeys.save();
59  CLogMessage(this).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 CActionHotkey actionHotkey =
97  indexHotkey.data(CActionHotkeyListModel::ActionHotkeyRole).value<CActionHotkey>();
98  const CActionHotkey selectedActionHotkey =
99  CHotkeyDialog::getActionHotkey(actionHotkey, getAllIdentifiers(), this);
100  if (selectedActionHotkey.isValid() && checkAndConfirmConflicts(selectedActionHotkey, { actionHotkey }))
101  {
102  updateHotkeyInSettings(actionHotkey, selectedActionHotkey);
103  m_model.setData(indexHotkey, QVariant::fromValue(selectedActionHotkey),
104  CActionHotkeyListModel::ActionHotkeyRole);
105 
106  // T784, further info about the "key"/button
107  CLogMessage(this).debug(u"%1, edited key: '%2'")
108  << classNameShort(this) << selectedActionHotkey.toQString(true);
109  }
110  this->resizeView();
111  }
112 
113  void CSettingsHotkeyComponent::removeEntry()
114  {
115  const QModelIndexList indexes = ui->tv_Hotkeys->selectionModel()->selectedRows();
116  for (const auto &index : indexes)
117  {
118  const CActionHotkey actionHotkey =
119  index.data(CActionHotkeyListModel::ActionHotkeyRole).value<CActionHotkey>();
120  removeHotkeyFromSettings(actionHotkey);
121  m_model.removeRows(index.row(), 1, QModelIndex());
122  }
123  this->resizeView();
124  }
125 
126  void CSettingsHotkeyComponent::addHotkeyToSettings(const CActionHotkey &actionHotkey)
127  {
128  CActionHotkeyList actionHotkeyList(m_actionHotkeys.getThreadLocal());
129  actionHotkeyList.push_back(actionHotkey);
130  m_actionHotkeys.set(actionHotkeyList);
131  }
132 
133  void CSettingsHotkeyComponent::updateHotkeyInSettings(const CActionHotkey &oldValue, const CActionHotkey &newValue)
134  {
136  this->removeHotkeyFromSettings(oldValue);
137  this->addHotkeyToSettings(newValue);
138 
139  // CActionHotkeyList actionHotkeyList(m_actionHotkeys.getThreadLocal());
140  // actionHotkeyList.replace(oldValue, newValue);
141  // m_actionHotkeys.set(actionHotkeyList);
142  }
143 
144  void CSettingsHotkeyComponent::removeHotkeyFromSettings(const CActionHotkey &actionHotkey)
145  {
146  CActionHotkeyList actionHotkeyList(m_actionHotkeys.getThreadLocal());
147  actionHotkeyList.remove(actionHotkey);
148  m_actionHotkeys.set(actionHotkeyList);
149  }
150 
151  bool CSettingsHotkeyComponent::checkAndConfirmConflicts(const CActionHotkey &actionHotkey,
152  const CActionHotkeyList &ignore)
153  {
154  // check the hotkeys of the same machine only
155  // and avoid duplicates (replace or add)
156  const CActionHotkeyList configuredHotkeysSameMachine =
157  m_actionHotkeys.getThreadLocal().findBySameMachine(actionHotkey);
158  CActionHotkeyList conflicts = configuredHotkeysSameMachine.findSupersetsOf(actionHotkey);
159  conflicts.replaceOrAdd(configuredHotkeysSameMachine.findSubsetsOf(actionHotkey));
160  conflicts.removeIfIn(ignore.findBySameMachine(actionHotkey));
161 
162  if (!conflicts.isEmpty())
163  {
164  QString message =
165  QStringLiteral("The selected combination conflicts with the following %1 combination(s):\n\n")
166  .arg(conflicts.size());
167  for (const CActionHotkey &conflict : conflicts)
168  {
169  message += conflict.toQString();
170  message += "\n";
171  }
172  message += "\n Do you want to use it anway?";
173  const auto reply = QMessageBox::warning(this, "SettingsHotkeyComponent", message,
174  QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
175  if (reply == QMessageBox::No) { return false; }
176  }
177  return true;
178  }
179 
181  {
182  const CActionHotkeyList hotkeys = m_actionHotkeys.getThreadLocal();
183  m_model.clear();
184 
185  // list of all defined hotkeys (not the dialog)
186  for (const CActionHotkey &hotkey : hotkeys)
187  {
188  const int position = m_model.rowCount();
189  m_model.insertRows(position, 1, QModelIndex());
190  const QModelIndex index = m_model.index(position, 0, QModelIndex());
191  m_model.setData(index, QVariant::fromValue(hotkey), CActionHotkeyListModel::ActionHotkeyRole);
192  }
193  this->resizeView();
194  }
195 
196  CIdentifierList CSettingsHotkeyComponent::getAllIdentifiers() const
197  {
198  CIdentifierList identifiers;
199  if (!sGui || !sGui->getIContextApplication()) { return identifiers; }
201  {
203  }
204 
205  // add local application
206  identifiers.push_back(CIdentifier("local identifer for hotkeys"));
207  return identifiers;
208  }
209 
210  void CSettingsHotkeyComponent::resizeView() { ui->tv_Hotkeys->resizeRowsToContents(); }
211 
212  void CSettingsHotkeyComponent::hotkeySlot(bool keyDown)
213  {
214  if (keyDown)
215  {
216  QMessageBox *msgBox = new QMessageBox(this);
217  msgBox->setAttribute(Qt::WA_DeleteOnClose);
218  msgBox->setStandardButtons(QMessageBox::Ok);
219  msgBox->setWindowTitle("Test");
220  msgBox->setText("Hotkey test");
221  msgBox->setIcon(QMessageBox::Information);
222  msgBox->setModal(false);
223  msgBox->open();
224  }
225  }
226 
228  {
229  Q_ASSERT_X(m_config, Q_FUNC_INFO, "Missing configuration");
230  if (CConfigurationWizard::lastWizardStepSkipped(this->wizard())) { return true; }
231  m_config->saveSettings();
232  return true;
233  }
234 
236  {
237  Q_ASSERT_X(m_config, Q_FUNC_INFO, "Missing configuration");
238  m_config->reloadHotkeysFromSettings();
239  }
240 } // 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
static void preformatted(const CStatusMessage &statusMessage)
Sends a verbatim, preformatted message to the log.
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:76
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:13
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.