#include "shortcutseditor.h" #include "ui_shortcutseditor.h" #include "config.h" #include "multikeyedit.h" #include "log.h" #include #include #include #include #include #include #include ShortcutsEditor::ShortcutsEditor(QWidget *parent) : QMainWindow(parent), ui(new Ui::ShortcutsEditor), main_container(nullptr) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); main_container = ui->scrollAreaWidgetContents_Shortcuts; auto *main_layout = new QVBoxLayout(main_container); main_layout->setSpacing(12); connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &ShortcutsEditor::dialogButtonClicked); } ShortcutsEditor::ShortcutsEditor(const QObjectList &shortcutableObjects, QWidget *parent) : ShortcutsEditor(parent) { setShortcutableObjects(shortcutableObjects); } ShortcutsEditor::~ShortcutsEditor() { delete ui; } void ShortcutsEditor::setShortcutableObjects(const QObjectList &shortcutableObjects) { parseObjectList(shortcutableObjects); populateMainContainer(); } void ShortcutsEditor::saveShortcuts() { QMultiMap objects_keySequences; for (auto it = multiKeyEdits_objects.cbegin(); it != multiKeyEdits_objects.cend(); ++it) { if (it.key()->keySequences().isEmpty()) objects_keySequences.insert(it.value(), QKeySequence()); for (auto keySequence : it.key()->keySequences()) objects_keySequences.insert(it.value(), keySequence); } shortcutsConfig.setUserShortcuts(objects_keySequences); shortcutsConfig.save(); emit shortcutsSaved(); } // Restores default shortcuts but doesn't save until Apply or OK is clicked. void ShortcutsEditor::resetShortcuts() { for (auto it = multiKeyEdits_objects.begin(); it != multiKeyEdits_objects.end(); ++it) { it.key()->blockSignals(true); const auto defaults = shortcutsConfig.defaultShortcuts(it.value()); it.key()->setKeySequences(defaults); it.key()->blockSignals(false); } } void ShortcutsEditor::parseObjectList(const QObjectList &objectList) { for (auto *object : objectList) { const auto label = getLabel(object); if (!label.isEmpty() && !object->objectName().isEmpty() && !object->objectName().startsWith("_q_")) labels_objects.insert(label, object); } } QString ShortcutsEditor::getLabel(const QObject *object) const { if (stringPropertyIsNotEmpty(object, "text")) return object->property("text").toString().remove('&'); else if (stringPropertyIsNotEmpty(object, "whatsThis")) return object->property("whatsThis").toString().remove('&'); else return QString(); } bool ShortcutsEditor::stringPropertyIsNotEmpty(const QObject *object, const char *name) const { return object->property(name).isValid() && !object->property(name).toString().isEmpty(); } void ShortcutsEditor::populateMainContainer() { for (auto object : labels_objects) { const auto shortcutContext = getShortcutContext(object); if (!contexts_layouts.contains(shortcutContext)) addNewContextGroup(shortcutContext); addNewMultiKeyEdit(object, shortcutContext); } } // The context for which the object's shortcut is active (Displayed in group box title). // Uses the parent window's objectName and adds spaces between words. QString ShortcutsEditor::getShortcutContext(const QObject *object) const { auto objectParentWidget = static_cast(object->parent()); auto context = objectParentWidget->window()->objectName(); static const QRegularExpression re("[A-Z]"); int i = context.indexOf(re, 1); while (i != -1) { if (context.at(i - 1) != ' ') context.insert(i++, ' '); i = context.indexOf(re, i + 1); } return context; } // Seperate shortcuts into context groups for duplicate checking. void ShortcutsEditor::addNewContextGroup(const QString &shortcutContext) { auto *groupBox = new QGroupBox(shortcutContext, main_container); main_container->layout()->addWidget(groupBox); auto *formLayout = new QFormLayout(groupBox); contexts_layouts.insert(shortcutContext, formLayout); } void ShortcutsEditor::addNewMultiKeyEdit(const QObject *object, const QString &shortcutContext) { auto *container = contexts_layouts.value(shortcutContext)->parentWidget(); auto *multiKeyEdit = new MultiKeyEdit(container); multiKeyEdit->setKeySequences(shortcutsConfig.userShortcuts(object)); connect(multiKeyEdit, &MultiKeyEdit::keySequenceChanged, this, &ShortcutsEditor::checkForDuplicates); contexts_layouts.value(shortcutContext)->addRow(labels_objects.key(object), multiKeyEdit); multiKeyEdits_objects.insert(multiKeyEdit, object); } void ShortcutsEditor::checkForDuplicates(const QKeySequence &keySequence) { if (keySequence.isEmpty()) return; auto *sender_multiKeyEdit = qobject_cast(sender()); if (!sender_multiKeyEdit) return; for (auto *sibling_multiKeyEdit : siblings(sender_multiKeyEdit)) if (sibling_multiKeyEdit->contains(keySequence)) promptUserOnDuplicateFound(sender_multiKeyEdit, sibling_multiKeyEdit); } QList ShortcutsEditor::siblings(MultiKeyEdit *multiKeyEdit) const { auto list = multiKeyEdit->parent()->findChildren(QString(), Qt::FindDirectChildrenOnly); list.removeOne(multiKeyEdit); return list; } void ShortcutsEditor::promptUserOnDuplicateFound(MultiKeyEdit *sender, MultiKeyEdit *sibling) { const auto duplicateKeySequence = sender->keySequences().last(); const auto siblingLabel = getLabel(multiKeyEdits_objects.value(sibling)); const auto message = QString( "Shortcut '%1' is already used by '%2', would you like to replace it?") .arg(duplicateKeySequence.toString()).arg(siblingLabel); const auto result = QMessageBox::question( this, "porymap", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (result == QMessageBox::Yes) removeKeySequence(duplicateKeySequence, sibling); else removeKeySequence(duplicateKeySequence, sender); activateWindow(); } void ShortcutsEditor::removeKeySequence(const QKeySequence &keySequence, MultiKeyEdit *multiKeyEdit) { multiKeyEdit->blockSignals(true); multiKeyEdit->removeOne(keySequence); multiKeyEdit->blockSignals(false); } void ShortcutsEditor::dialogButtonClicked(QAbstractButton *button) { auto buttonRole = ui->buttonBox->buttonRole(button); if (buttonRole == QDialogButtonBox::AcceptRole) { saveShortcuts(); close(); } else if (buttonRole == QDialogButtonBox::ApplyRole) { saveShortcuts(); } else if (buttonRole == QDialogButtonBox::RejectRole) { close(); } else if (buttonRole == QDialogButtonBox::ResetRole) { resetShortcuts(); } }