From a1034af2e8f5cf8f14cf0288d19694ba827d3320 Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 14 Feb 2014 19:45:35 +0100 Subject: [PATCH] Core: Mark shortcut conflicts in keyboard settings input box Task-number: QTCREATORBUG-6 Change-Id: Ife1f97d24733814e7512dd8bc6584a1d72f66fa8 Reviewed-by: Christian Stenger Reviewed-by: Eike Ziller --- .../actionmanager/commandmappings.cpp | 248 +++++++++++++----- .../actionmanager/commandmappings.h | 14 +- .../actionmanager/commandmappings.ui | 165 ------------ src/plugins/coreplugin/coreplugin.pro | 1 - src/plugins/coreplugin/coreplugin.qbs | 2 +- .../coreplugin/dialogs/shortcutsettings.cpp | 114 ++++---- .../coreplugin/dialogs/shortcutsettings.h | 7 +- 7 files changed, 262 insertions(+), 289 deletions(-) delete mode 100644 src/plugins/coreplugin/actionmanager/commandmappings.ui diff --git a/src/plugins/coreplugin/actionmanager/commandmappings.cpp b/src/plugins/coreplugin/actionmanager/commandmappings.cpp index 0b31e5cfb30..6082127529a 100644 --- a/src/plugins/coreplugin/actionmanager/commandmappings.cpp +++ b/src/plugins/coreplugin/actionmanager/commandmappings.cpp @@ -28,142 +28,254 @@ ****************************************************************************/ #include "commandmappings.h" -#include "ui_commandmappings.h" #include "commandsfile.h" + #include #include #include +#include +#include -#include #include +#include +#include +#include +#include +#include +#include +#include Q_DECLARE_METATYPE(Core::Internal::ShortcutItem*) -using namespace Core; -using namespace Core::Internal; +using namespace Utils; + +namespace Core { +namespace Internal { + +class KeySequenceValidator : public FancyLineEdit +{ +public: + KeySequenceValidator(QWidget *parent, CommandMappings *mappings) + : FancyLineEdit(parent), m_mappings(mappings) + {} + + bool validate(const QString &, QString *) const + { + return !m_mappings->hasConflicts(); + } + + CommandMappings *m_mappings; +}; + +class CommandMappingsPrivate +{ +public: + CommandMappingsPrivate(CommandMappings *parent) + : q(parent), m_widget(0) + {} + + void setupWidget() + { + QTC_CHECK(m_widget == 0); + m_widget = new QWidget; + + groupBox = new QGroupBox(m_widget); + groupBox->setTitle(CommandMappings::tr("Command Mappings")); + + filterEdit = new FancyLineEdit(groupBox); + filterEdit->setFiltering(true); + + commandList = new QTreeWidget(groupBox); + commandList->setRootIsDecorated(false); + commandList->setUniformRowHeights(true); + commandList->setSortingEnabled(true); + commandList->setColumnCount(3); + + QTreeWidgetItem *item = commandList->headerItem(); + item->setText(2, CommandMappings::tr("Target")); + item->setText(1, CommandMappings::tr("Label")); + item->setText(0, CommandMappings::tr("Command")); + + defaultButton = new QPushButton(CommandMappings::tr("Reset All"), groupBox); + defaultButton->setToolTip(CommandMappings::tr("Reset all to default")); + + importButton = new QPushButton(CommandMappings::tr("Import..."), groupBox); + exportButton = new QPushButton(CommandMappings::tr("Export..."), groupBox); + + targetEditGroup = new QGroupBox(CommandMappings::tr("Target Identifier"), m_widget); + + targetEdit = new KeySequenceValidator(targetEditGroup, q); + targetEdit->setAutoHideButton(FancyLineEdit::Right, true); + targetEdit->setPlaceholderText(QString()); + targetEdit->installEventFilter(q); + targetEdit->setFiltering(true); + + resetButton = new QPushButton(targetEditGroup); + resetButton->setToolTip(CommandMappings::tr("Reset to default")); + resetButton->setText(CommandMappings::tr("Reset")); + + QLabel *infoLabel = new QLabel(targetEditGroup); + infoLabel->setTextFormat(Qt::RichText); + + QHBoxLayout *hboxLayout1 = new QHBoxLayout(); + hboxLayout1->addWidget(defaultButton); + hboxLayout1->addStretch(); + hboxLayout1->addWidget(importButton); + hboxLayout1->addWidget(exportButton); + + QHBoxLayout *hboxLayout = new QHBoxLayout(); + hboxLayout->addWidget(filterEdit); + + QVBoxLayout *vboxLayout1 = new QVBoxLayout(groupBox); + vboxLayout1->addLayout(hboxLayout); + vboxLayout1->addWidget(commandList); + vboxLayout1->addLayout(hboxLayout1); + + QHBoxLayout *hboxLayout2 = new QHBoxLayout(); + hboxLayout2->addWidget(new QLabel(CommandMappings::tr("Target:"), targetEditGroup)); + hboxLayout2->addWidget(targetEdit); + hboxLayout2->addWidget(resetButton); + + QVBoxLayout *vboxLayout2 = new QVBoxLayout(targetEditGroup); + vboxLayout2->addLayout(hboxLayout2); + vboxLayout2->addWidget(infoLabel); + + QVBoxLayout *vboxLayout = new QVBoxLayout(m_widget); + vboxLayout->addWidget(groupBox); + vboxLayout->addWidget(targetEditGroup); + + q->connect(targetEdit, SIGNAL(buttonClicked(Utils::FancyLineEdit::Side)), + SLOT(removeTargetIdentifier())); + q->connect(resetButton, SIGNAL(clicked()), + SLOT(resetTargetIdentifier())); + q->connect(exportButton, SIGNAL(clicked()), + SLOT(exportAction())); + q->connect(importButton, SIGNAL(clicked()), + SLOT(importAction())); + q->connect(defaultButton, SIGNAL(clicked()), + SLOT(defaultAction())); + + q->initialize(); + + commandList->sortByColumn(0, Qt::AscendingOrder); + + q->connect(filterEdit, SIGNAL(textChanged(QString)), + SLOT(filterChanged(QString))); + q->connect(commandList, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), + SLOT(commandChanged(QTreeWidgetItem*))); + q->connect(targetEdit, SIGNAL(textChanged(QString)), + SLOT(targetIdentifierChanged())); + + new HeaderViewStretcher(commandList->header(), 1); + + q->commandChanged(0); + } + + CommandMappings *q; + QPointer m_widget; + + QGroupBox *groupBox; + FancyLineEdit *filterEdit; + QTreeWidget *commandList; + QPushButton *defaultButton; + QPushButton *importButton; + QPushButton *exportButton; + QGroupBox *targetEditGroup; + FancyLineEdit *targetEdit; + QPushButton *resetButton; +}; + +} // namespace Internal CommandMappings::CommandMappings(QObject *parent) - : IOptionsPage(parent), m_page(0) + : IOptionsPage(parent), d(new Internal::CommandMappingsPrivate(this)) { } -// IOptionsPage +CommandMappings::~CommandMappings() +{ + delete d; +} QWidget *CommandMappings::widget() { - if (!m_widget) { - m_page = new Ui::CommandMappings(); - m_widget = new QWidget; - m_page->setupUi(m_widget); - m_page->targetEdit->setAutoHideButton(Utils::FancyLineEdit::Right, true); - m_page->targetEdit->setPlaceholderText(QString()); - m_page->targetEdit->installEventFilter(this); - m_page->targetEdit->setFiltering(true); - - m_page->filterEdit->setFiltering(true); - - connect(m_page->targetEdit, SIGNAL(buttonClicked(Utils::FancyLineEdit::Side)), - this, SLOT(removeTargetIdentifier())); - connect(m_page->resetButton, SIGNAL(clicked()), - this, SLOT(resetTargetIdentifier())); - connect(m_page->exportButton, SIGNAL(clicked()), - this, SLOT(exportAction())); - connect(m_page->importButton, SIGNAL(clicked()), - this, SLOT(importAction())); - connect(m_page->defaultButton, SIGNAL(clicked()), - this, SLOT(defaultAction())); - - initialize(); - - m_page->commandList->sortByColumn(0, Qt::AscendingOrder); - - connect(m_page->filterEdit, SIGNAL(textChanged(QString)), - this, SLOT(filterChanged(QString))); - connect(m_page->commandList, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), - this, SLOT(commandChanged(QTreeWidgetItem*))); - connect(m_page->targetEdit, SIGNAL(textChanged(QString)), - this, SLOT(targetIdentifierChanged())); - - new Utils::HeaderViewStretcher(m_page->commandList->header(), 1); - - commandChanged(0); - } - return m_widget; + if (!d->m_widget) + d->setupWidget(); + return d->m_widget; } void CommandMappings::setImportExportEnabled(bool enabled) { - m_page->importButton->setVisible(enabled); - m_page->exportButton->setVisible(enabled); + d->importButton->setVisible(enabled); + d->exportButton->setVisible(enabled); } QTreeWidget *CommandMappings::commandList() const { - return m_page->commandList; + return d->commandList; } QLineEdit *CommandMappings::targetEdit() const { - return m_page->targetEdit; + return d->targetEdit; } void CommandMappings::setPageTitle(const QString &s) { - m_page->groupBox->setTitle(s); + d->groupBox->setTitle(s); } void CommandMappings::setTargetLabelText(const QString &s) { - m_page->targetEditLabel->setText(s); + d->targetEdit->setText(s); } void CommandMappings::setTargetEditTitle(const QString &s) { - m_page->targetEditGroup->setTitle(s); + d->targetEditGroup->setTitle(s); } void CommandMappings::setTargetHeader(const QString &s) { - m_page->commandList->setHeaderLabels(QStringList() << tr("Command") << tr("Label") << s); + d->commandList->setHeaderLabels(QStringList() << tr("Command") << tr("Label") << s); } void CommandMappings::finish() { - delete m_widget; - if (!m_page) // page was never shown - return; - delete m_page; - m_page = 0; + delete d->m_widget; } void CommandMappings::commandChanged(QTreeWidgetItem *current) { if (!current || !current->data(0, Qt::UserRole).isValid()) { - m_page->targetEdit->setText(QString()); - m_page->targetEditGroup->setEnabled(false); + d->targetEdit->setText(QString()); + d->targetEditGroup->setEnabled(false); return; } - m_page->targetEditGroup->setEnabled(true); + d->targetEditGroup->setEnabled(true); } void CommandMappings::filterChanged(const QString &f) { - if (!m_page) - return; - for (int i=0; icommandList->topLevelItemCount(); ++i) { - QTreeWidgetItem *item = m_page->commandList->topLevelItem(i); + for (int i = 0; i < d->commandList->topLevelItemCount(); ++i) { + QTreeWidgetItem *item = d->commandList->topLevelItem(i); filter(f, item); } } +bool CommandMappings::hasConflicts() const +{ + return true; +} + bool CommandMappings::filter(const QString &filterString, QTreeWidgetItem *item) { bool visible = filterString.isEmpty(); int columnCount = item->columnCount(); for (int i = 0; !visible && i < columnCount; ++i) { QString text = item->text(i); - if (Utils::HostOsInfo::isMacHost()) { + if (HostOsInfo::isMacHost()) { // accept e.g. Cmd+E in the filter. the text shows special fancy characters for Cmd if (i == columnCount - 1) { QKeySequence key = QKeySequence::fromString(text, QKeySequence::NativeText); @@ -203,7 +315,7 @@ void CommandMappings::setModified(QTreeWidgetItem *item , bool modified) QString CommandMappings::filterText() const { - if (!m_page) - return QString(); - return m_page->filterEdit->text(); + return d->filterEdit ? d->filterEdit->text() : QString(); } + +} // namespace Core diff --git a/src/plugins/coreplugin/actionmanager/commandmappings.h b/src/plugins/coreplugin/actionmanager/commandmappings.h index df3ce639c9f..326e0a68b00 100644 --- a/src/plugins/coreplugin/actionmanager/commandmappings.h +++ b/src/plugins/coreplugin/actionmanager/commandmappings.h @@ -33,7 +33,6 @@ #include #include -#include QT_BEGIN_NAMESPACE class QLineEdit; @@ -41,9 +40,11 @@ class QTreeWidget; class QTreeWidgetItem; QT_END_NAMESPACE +namespace Utils { class FancyLineEdit; } + namespace Core { -namespace Internal { namespace Ui { class CommandMappings; } } +namespace Internal { class CommandMappingsPrivate; } class CORE_EXPORT CommandMappings : public Core::IOptionsPage { @@ -51,6 +52,8 @@ class CORE_EXPORT CommandMappings : public Core::IOptionsPage public: CommandMappings(QObject *parent = 0); + ~CommandMappings(); + virtual bool hasConflicts() const; protected slots: void commandChanged(QTreeWidgetItem *current); @@ -78,11 +81,10 @@ protected: void setTargetEditTitle(const QString &s); void setTargetHeader(const QString &s); void setModified(QTreeWidgetItem *item, bool modified); - virtual void markPossibleCollisions(QTreeWidgetItem *) {} - virtual void resetCollisionMarkers() {} + private: - QPointer m_widget; - Internal::Ui::CommandMappings *m_page; + friend class Internal::CommandMappingsPrivate; + Internal::CommandMappingsPrivate *d; }; } // namespace Core diff --git a/src/plugins/coreplugin/actionmanager/commandmappings.ui b/src/plugins/coreplugin/actionmanager/commandmappings.ui deleted file mode 100644 index 3eabb66bdf0..00000000000 --- a/src/plugins/coreplugin/actionmanager/commandmappings.ui +++ /dev/null @@ -1,165 +0,0 @@ - - - Core::Internal::CommandMappings - - - - 0 - 0 - 568 - 451 - - - - - - - Command Mappings - - - - - - - - - - - - - false - - - true - - - true - - - 3 - - - - Command - - - - - Label - - - - - Target - - - - - - - - - - Reset all to default - - - Reset All - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Import... - - - - - - - Export... - - - - - - - - - - - - Target Identifier - - - - - - - - Target: - - - - - - - - - - - 0 - 0 - - - - Reset to default - - - Reset - - - - 16 - 16 - - - - - - - - - - Qt::RichText - - - - - - - - - - - Utils::FancyLineEdit - QLineEdit -
utils/fancylineedit.h
-
-
- - -
diff --git a/src/plugins/coreplugin/coreplugin.pro b/src/plugins/coreplugin/coreplugin.pro index 928b597aa62..d38f93fbe61 100644 --- a/src/plugins/coreplugin/coreplugin.pro +++ b/src/plugins/coreplugin/coreplugin.pro @@ -197,7 +197,6 @@ HEADERS += mainwindow.h \ dialogs/addtovcsdialog.h FORMS += dialogs/newdialog.ui \ - actionmanager/commandmappings.ui \ dialogs/saveitemsdialog.ui \ dialogs/readonlyfilesdialog.ui \ dialogs/openwithdialog.ui \ diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs index a8ffcb6dae6..23c10f9c2d0 100644 --- a/src/plugins/coreplugin/coreplugin.qbs +++ b/src/plugins/coreplugin/coreplugin.qbs @@ -102,7 +102,7 @@ QtcPlugin { "actionmanager.cpp", "actionmanager.h", "actionmanager_p.h", "command.cpp", "command.h", "command_p.h", "commandbutton.cpp", "commandbutton.h", - "commandmappings.cpp", "commandmappings.h", "commandmappings.ui", + "commandmappings.cpp", "commandmappings.h", "commandsfile.cpp", "commandsfile.h", ] } diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp index 6150e42bf17..c9d1e996d85 100644 --- a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp +++ b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp @@ -37,6 +37,8 @@ #include #include +#include + #include #include #include @@ -127,6 +129,7 @@ void ShortcutSettings::commandChanged(QTreeWidgetItem *current) return; ShortcutItem *scitem = qvariant_cast(current->data(0, Qt::UserRole)); setKeySequence(scitem->m_key); + markCollisions(scitem); } void ShortcutSettings::targetIdentifierChanged() @@ -140,11 +143,47 @@ void ShortcutSettings::targetIdentifierChanged() else setModified(current, false); current->setText(2, scitem->m_key.toString(QKeySequence::NativeText)); - resetCollisionMarker(scitem); - markPossibleCollisions(scitem); + markCollisions(scitem); } } +bool ShortcutSettings::hasConflicts() const +{ + QTreeWidgetItem *current = commandList()->currentItem(); + if (!current || !current->data(0, Qt::UserRole).isValid()) + return false; + + ShortcutItem *item = qvariant_cast(current->data(0, Qt::UserRole)); + if (!item) + return false; + + const QKeySequence currentKey = QKeySequence::fromString(targetEdit()->text(), QKeySequence::NativeText); + if (currentKey.isEmpty()) + return false; + + const Id globalId(Constants::C_GLOBAL); + const Context itemContext = item->m_cmd->context(); + + foreach (ShortcutItem *listItem, m_scitems) { + if (item == listItem) + continue; + if (listItem->m_key.isEmpty()) + continue; + if (listItem->m_key.matches(currentKey) == QKeySequence::NoMatch) + continue; + + const Context listContext = listItem->m_cmd->context(); + if (itemContext.contains(globalId) && !listContext.isEmpty()) + return true; + if (listContext.contains(globalId) && !itemContext.isEmpty()) + return true; + foreach (Id id, listContext) + if (itemContext.contains(id)) + return true; + } + return false; +} + void ShortcutSettings::setKeySequence(const QKeySequence &key) { m_key[0] = m_key[1] = m_key[2] = m_key[3] = 0; @@ -168,6 +207,9 @@ void ShortcutSettings::removeTargetIdentifier() { m_keyNum = m_key[0] = m_key[1] = m_key[2] = m_key[3] = 0; targetEdit()->clear(); + + foreach (ShortcutItem *item, m_scitems) + markCollisions(item); } void ShortcutSettings::importAction() @@ -195,10 +237,8 @@ void ShortcutSettings::importAction() } } - foreach (ShortcutItem *item, m_scitems) { - resetCollisionMarker(item); - markPossibleCollisions(item); - } + foreach (ShortcutItem *item, m_scitems) + markCollisions(item); } } @@ -212,10 +252,8 @@ void ShortcutSettings::defaultAction() commandChanged(item->m_item); } - foreach (ShortcutItem *item, m_scitems) { - resetCollisionMarker(item); - markPossibleCollisions(item); - } + foreach (ShortcutItem *item, m_scitems) + markCollisions(item); } void ShortcutSettings::exportAction() @@ -247,7 +285,7 @@ void ShortcutSettings::initialize() clear(); QMap sections; - foreach (Command *c, ActionManager::instance()->commands()) { + foreach (Command *c, ActionManager::commands()) { if (c->hasAttribute(Command::CA_NonConfigurable)) continue; if (c->action() && c->action()->isSeparator()) @@ -283,7 +321,7 @@ void ShortcutSettings::initialize() item->setData(0, Qt::UserRole, qVariantFromValue(s)); - markPossibleCollisions(s); + markCollisions(s); } filterChanged(filterText()); } @@ -341,44 +379,32 @@ int ShortcutSettings::translateModifiers(Qt::KeyboardModifiers state, return result; } -void ShortcutSettings::markPossibleCollisions(ShortcutItem *item) +void ShortcutSettings::markCollisions(ShortcutItem *item) { - if (item->m_key.isEmpty()) - return; + bool hasCollision = false; + if (!item->m_key.isEmpty()) { + Id globalId = Context(Constants::C_GLOBAL).at(0); + const Context itemContext = item->m_cmd->context(); - Id globalId = Context(Constants::C_GLOBAL).at(0); + foreach (ShortcutItem *currentItem, m_scitems) { - foreach (ShortcutItem *currentItem, m_scitems) { - - if (currentItem->m_key.isEmpty() || item == currentItem || - item->m_key != currentItem->m_key) { - continue; - } - - foreach (Id id, currentItem->m_cmd->context()) { - // conflict if context is identical, OR if one - // of the contexts is the global context - if (item->m_cmd->context().contains(id) || - (item->m_cmd->context().contains(globalId) && - !currentItem->m_cmd->context().isEmpty()) || - (currentItem->m_cmd->context().contains(globalId) && - !item->m_cmd->context().isEmpty())) { - currentItem->m_item->setForeground(2, Qt::red); - item->m_item->setForeground(2, Qt::red); + if (currentItem->m_key.isEmpty() || item == currentItem + || item->m_key != currentItem->m_key) + continue; + foreach (Id id, currentItem->m_cmd->context()) { + // conflict if context is identical, OR if one + // of the contexts is the global context + const Context thisContext = currentItem->m_cmd->context(); + if (itemContext.contains(id) + || (itemContext.contains(globalId) && !thisContext.isEmpty()) + || (thisContext.contains(globalId) && !itemContext.isEmpty())) { + currentItem->m_item->setForeground(2, Qt::red); + hasCollision = true; + } } } } + item->m_item->setForeground(2, hasCollision ? Qt::red : commandList()->palette().foreground()); } -void ShortcutSettings::resetCollisionMarker(ShortcutItem *item) -{ - item->m_item->setForeground(2, commandList()->palette().foreground()); -} - - -void ShortcutSettings::resetCollisionMarkers() -{ - foreach (ShortcutItem *item, m_scitems) - resetCollisionMarker(item); -} diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.h b/src/plugins/coreplugin/dialogs/shortcutsettings.h index 26681bd250c..7f776ea2134 100644 --- a/src/plugins/coreplugin/dialogs/shortcutsettings.h +++ b/src/plugins/coreplugin/dialogs/shortcutsettings.h @@ -85,10 +85,9 @@ private: void handleKeyEvent(QKeyEvent *e); int translateModifiers(Qt::KeyboardModifiers state, const QString &text); - using Core::CommandMappings::markPossibleCollisions; - void markPossibleCollisions(ShortcutItem *); - void resetCollisionMarker(ShortcutItem *); - void resetCollisionMarkers(); + + void markCollisions(ShortcutItem *); + bool hasConflicts() const; QList m_scitems; int m_key[4], m_keyNum;