/* Copyright (C) 2014 * swift project Community / Contributors * * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, * or distributed except according to the terms contained in the LICENSE file. */ #include "blackgui/components/hotkeydialog.h" #include "blackgui/guiapplication.h" #include "blackgui/stylesheetutility.h" #include "blackcore/context/contextapplication.h" #include "blackcore/inputmanager.h" #include "blackmisc/icons.h" #include "blackmisc/identifier.h" #include "blackmisc/input/hotkeycombination.h" #include "blackmisc/input/keyboardkeylist.h" #include "blackmisc/logmessage.h" #include "blackmisc/statusmessage.h" #include "ui_hotkeydialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace BlackMisc; using namespace BlackMisc::Input; using namespace BlackCore; using namespace BlackGui::Models; namespace BlackGui { namespace Components { CHotkeyDialog::CHotkeyDialog(const CActionHotkey &actionHotkey, const CIdentifierList &identifiers, QWidget *parent) : QDialog(parent), ui(new Ui::CHotkeyDialog), m_actionHotkey(actionHotkey), m_actionModel(this) { setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); ui->setupUi(this); ui->qf_Advanced->hide(); ui->pb_AdvancedMode->setIcon(CIcons::arrowMediumSouth16()); ui->tv_Actions->setModel(&m_actionModel); selectAction(); CHotkeyCombination combination = actionHotkey.getCombination(); if (!combination.isEmpty()) { ui->pb_SelectedHotkey->setText(combination.toQString()); ui->pb_SelectedHotkey->setToolTip(combination.asStringWithDeviceNames()); } else { ui->pb_SelectedHotkey->setToolTip("Press to select an new combination..."); } // get all remote identifiers in case there is no key for a remote machine yet CIdentifierList registeredApplications; if (sGui && sGui->getIContextApplication()) { registeredApplications = sGui->getIContextApplication()->getRegisteredApplications(); const CIdentifier appIdentifier = sGui->getIContextApplication()->getApplicationIdentifier(); registeredApplications.push_back(appIdentifier); } CIdentifierList machineIdentifiers(identifiers); machineIdentifiers.push_back(registeredApplications); // add the registered applications if (actionHotkey.isValid()) { machineIdentifiers.push_back(actionHotkey.getApplicableMachine()); } const CIdentifierList machineIdentifiersUnique = machineIdentifiers.getMachinesUnique(); int index = -1; for (const CIdentifier &app : machineIdentifiersUnique) { ui->cb_Identifier->addItem(app.getMachineName(), QVariant::fromValue(app)); if (m_actionHotkey.getApplicableMachine().hasSameMachineName(app)) { index = ui->cb_Identifier->count() - 1; } } if (index < 0 && ui->cb_Identifier->count() > 0) { // if nothing was found ui->cb_Identifier->setCurrentIndex(0); } else if (index != ui->cb_Identifier->currentIndex()) { ui->cb_Identifier->setCurrentIndex(index); } connect(ui->pb_AdvancedMode, &QPushButton::clicked, this, &CHotkeyDialog::advancedModeChanged); connect(ui->pb_SelectedHotkey, &QPushButton::clicked, this, &CHotkeyDialog::captureHotkey); connect(ui->pb_Accept, &QPushButton::clicked, this, &CHotkeyDialog::accept); connect(ui->pb_Cancel, &QPushButton::clicked, this, &CHotkeyDialog::reject); connect(ui->tv_Actions->selectionModel(), &QItemSelectionModel::selectionChanged, this, &CHotkeyDialog::changeSelectedAction); connect(ui->cb_Identifier, qOverload(&QComboBox::currentIndexChanged), this, &CHotkeyDialog::changeApplicableMachine); if (sGui && sGui->getInputManager()) { connect(sGui->getInputManager(), &BlackCore::CInputManager::combinationSelectionChanged, this, &CHotkeyDialog::combinationSelectionChanged); connect(sGui->getInputManager(), &BlackCore::CInputManager::combinationSelectionFinished, this, &CHotkeyDialog::combinationSelectionFinished); } initStyleSheet(); } CHotkeyDialog::~CHotkeyDialog() { } CKeySelectionBox::CKeySelectionBox(QWidget *parent) : CHorizontalComboBox(parent) { connect(this, qOverload(&CKeySelectionBox::currentIndexChanged), this, &CKeySelectionBox::updateSelectedIndex); } void CKeySelectionBox::setSelectedIndex(int index) { m_oldIndex = index; setCurrentIndex(m_oldIndex); } void CKeySelectionBox::updateSelectedIndex(int index) { emit keySelectionChanged(m_oldIndex, index); m_oldIndex = index; } void CHotkeyDialog::initStyleSheet() { if (!sGui) { return; } const QString s = sGui->getStyleSheetUtility().styles( { CStyleSheetUtility::fileNameFonts(), CStyleSheetUtility::fileNameStandardWidget() } ); this->setStyleSheet(s); } CActionHotkey CHotkeyDialog::getActionHotkey(const CActionHotkey &initial, const CIdentifierList &identifiers, QWidget *parent) { CHotkeyDialog editDialog(initial, identifiers, parent); editDialog.setWindowModality(Qt::WindowModal); if (editDialog.exec()) { return editDialog.getSelectedActionHotkey(); } return {}; } void CHotkeyDialog::advancedModeChanged() { // if (m_actionHotkey.getCombination().isEmpty()) return; if (!ui->qf_Advanced->isVisible()) { setupAdvancedFrame(); ui->qf_Advanced->show(); ui->pb_AdvancedMode->setIcon(CIcons::arrowMediumNorth16()); } else { ui->pb_AdvancedMode->setIcon(CIcons::arrowMediumSouth16()); ui->qf_Advanced->hide(); ui->gb_Hotkey->resize(0, 0); } } void CHotkeyDialog::captureHotkey() { if (!sApp || sApp->isShuttingDown()) { return; } ui->pb_SelectedHotkey->setText("Press any key/button..."); ui->pb_SelectedHotkey->setToolTip({}); sApp->getInputManager()->startCapture(); } void CHotkeyDialog::combinationSelectionChanged(const CHotkeyCombination &combination) { ui->pb_SelectedHotkey->setText(combination.toQString()); ui->pb_SelectedHotkey->setToolTip(combination.asStringWithDeviceNames()); } void CHotkeyDialog::combinationSelectionFinished(const CHotkeyCombination &combination) { m_actionHotkey.setCombination(combination); synchronize(); } void CHotkeyDialog::changeSelectedAction(const QItemSelection &selected, const QItemSelection &deselected) { Q_UNUSED(deselected) if (selected.indexes().isEmpty()) { return; } const auto index = selected.indexes().first(); m_actionHotkey.setAction(index.data(CActionModel::ActionRole).toString()); } CKeySelectionBox *CHotkeyDialog::addSelectionBox(const CKeyboardKeyList &allSupportedKeys, const CKeyboardKey &keyboardKey) { int currentIndex = 0; const int width = qRound(1.5 * this->width()); const bool select = !keyboardKey.isUnknown(); CKeySelectionBox *ksb = new CKeySelectionBox(ui->qf_Advanced); ksb->addItem(noKeyButton(), QVariant::fromValue(CKeyboardKey())); // at front for (const CKeyboardKey &supportedKey : allSupportedKeys) { ksb->addItem(supportedKey.toQString(), QVariant::fromValue(supportedKey)); if (select && supportedKey == keyboardKey) { currentIndex = ksb->count() - 1; } } ksb->setSelectedIndex(currentIndex); ksb->setPopupWidth(qMin(width, 600)); ksb->addItem(noKeyButton(), QVariant::fromValue(CKeyboardKey())); // at back (easier to find it is there twice) ui->qf_Advanced->layout()->addWidget(ksb); const int position = ui->qf_Advanced->layout()->count() - 1; ksb->setProperty("position", position); connect(ksb, &CKeySelectionBox::keySelectionChanged, this, &CHotkeyDialog::advancedKeyChanged); return ksb; } CKeySelectionBox *CHotkeyDialog::addSelectionBox(const CJoystickButtonList &allAvailableButtons, const CJoystickButton &joystickButton) { int currentIndex = -1; const int width = qRound(1.5 * this->width()); CKeySelectionBox *ksb = new CKeySelectionBox(ui->qf_Advanced); ksb->addItem(noKeyButton(), QVariant::fromValue(CJoystickButton())); // at front for (const CJoystickButton &availableButton : allAvailableButtons) { ksb->addItem(availableButton.toQString(), QVariant::fromValue(availableButton)); if (availableButton == joystickButton) { currentIndex = ksb->count() - 1; ksb->setToolTip(joystickButton.getButtonAsStringWithDeviceName()); } } ksb->setSelectedIndex(currentIndex); ksb->setPopupWidth(qMin(width, 600)); ksb->addItem(noKeyButton(), QVariant::fromValue(CJoystickButton())); // at back (easier to find it is there twice) ui->qf_Advanced->layout()->addWidget(ksb); const int position = ui->qf_Advanced->layout()->count() - 1; ksb->setProperty("position", position); connect(ksb, &CKeySelectionBox::keySelectionChanged, this, &CHotkeyDialog::advancedKeyChanged); return ksb; } void CHotkeyDialog::changeApplicableMachine(int index) { Q_UNUSED(index) const QVariant userData = ui->cb_Identifier->currentData(); Q_ASSERT(userData.canConvert()); m_actionHotkey.setApplicableMachine(userData.value()); } void CHotkeyDialog::accept() { if (m_actionHotkey.getApplicableMachine().getMachineName().isEmpty()) { CLogMessage(this).validationWarning(u"Missing hotkey '%1'") << ui->gb_Machine->title(); return; } if (m_actionHotkey.getCombination().isEmpty()) { CLogMessage(this).validationWarning(u"Missing hotkey '%1'") << ui->gb_Hotkey->title(); return; } if (m_actionHotkey.getAction().isEmpty()) { CLogMessage(this).validationWarning(u"Missing hotkey '%1'") << ui->gb_Action->title(); return; } QDialog::accept(); } void CHotkeyDialog::synchronize() { synchronizeSimpleSelection(); synchronizeAdvancedSelection(); } void CHotkeyDialog::synchronizeSimpleSelection() { CHotkeyCombination combination = m_actionHotkey.getCombination(); ui->pb_SelectedHotkey->setText(combination.toQString()); ui->pb_SelectedHotkey->setToolTip(combination.asStringWithDeviceNames()); } void CHotkeyDialog::synchronizeAdvancedSelection() { if (ui->qf_Advanced->isVisible()) { setupAdvancedFrame(); } } void CHotkeyDialog::setupAdvancedFrame() { if (!sGui || sGui->isShuttingDown()) { return; } this->clearAdvancedFrame(); const CKeyboardKeyList allSupportedKeys = CKeyboardKeyList::allSupportedKeys(); const CJoystickButtonList allAvailableButtons = sGui->getInputManager()->getAllAvailableJoystickButtons(); const CKeyboardKeyList keyboardKeys = m_actionHotkey.getCombination().getKeyboardKeys(); int c = 0; for (const CKeyboardKey &keyboardKey : keyboardKeys) { this->addSelectionBox(allSupportedKeys, keyboardKey); c++; } const CJoystickButtonList joystickButtons = m_actionHotkey.getCombination().getJoystickButtons(); for (const CJoystickButton &joystickButton : joystickButtons) { this->addSelectionBox(allAvailableButtons, joystickButton); c++; } // add one box more so we can add keys/buttons if (c < 2) { this->addSelectionBox(allSupportedKeys); } } void CHotkeyDialog::clearAdvancedFrame() { QLayout *layout = ui->qf_Advanced->layout(); QLayoutItem *child; while ((child = layout->takeAt(0)) != nullptr) { if (child->widget()) child->widget()->deleteLater(); delete child; } } void CHotkeyDialog::advancedKeyChanged(int oldIndex, int newIndex) { const CKeySelectionBox *ksb = qobject_cast(sender()); Q_ASSERT(ksb); if (ksb->itemData(oldIndex).canConvert() && ksb->itemData(newIndex).canConvert()) { CKeyboardKey oldKey = ksb->itemData(oldIndex).value(); CKeyboardKey newKey = ksb->itemData(newIndex).value(); CHotkeyCombination combination = m_actionHotkey.getCombination(); if (newKey.isUnknown()) { combination.removeKeyboardKey(oldKey); } else { combination.replaceKey(oldKey, newKey); } m_actionHotkey.setCombination(combination); } if (ksb->itemData(oldIndex).canConvert() && ksb->itemData(newIndex).canConvert()) { CJoystickButton oldButton = ksb->itemData(oldIndex).value(); CJoystickButton newButton = ksb->itemData(newIndex).value(); CHotkeyCombination combination = m_actionHotkey.getCombination(); if (! newButton.isValid()) { combination.removeJoystickButton(oldButton); } else { combination.replaceButton(oldButton, newButton); } m_actionHotkey.setCombination(combination); } ui->pb_SelectedHotkey->setText(m_actionHotkey.getCombination().toQString()); ui->pb_SelectedHotkey->setToolTip(m_actionHotkey.getCombination().asStringWithDeviceNames()); } void CHotkeyDialog::selectAction() { if (m_actionHotkey.getAction().isEmpty()) { return; } const QStringList tokens = m_actionHotkey.getAction().split("/", Qt::SkipEmptyParts); QModelIndex parentIndex = QModelIndex(); for (const QString &token : tokens) { const QModelIndex startIndex = m_actionModel.index(0, 0, parentIndex); const QModelIndexList indexList = m_actionModel.match(startIndex, Qt::DisplayRole, QVariant::fromValue(token)); if (indexList.isEmpty()) { return; } parentIndex = indexList.first(); ui->tv_Actions->expand(parentIndex); } QItemSelectionModel *selectionModel = ui->tv_Actions->selectionModel(); selectionModel->select(parentIndex, QItemSelectionModel::Select); } const QString &CHotkeyDialog::noKeyButton() { static const QString k = "[none]"; return k; } } // ns } // ns