1 // Copyright (C) 2007-2024 CEA, EDF, OPEN CASCADE
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include "QtxShortcutEdit.h"
26 #include <QToolButton>
29 #include <QTableWidgetItem>
31 #include <QMessageBox>
32 #include <QPushButton>
37 #include <QKeySequence>
40 #define COLUMN_SIZE 500
43 QtxKeySequenceEdit::QtxKeySequenceEdit(QWidget* parent)
47 myKeySequenceLineEdit->installEventFilter(this);
50 /*! \brief Set a key sequence to edit. */
51 void QtxKeySequenceEdit::setConfirmedKeySequence(const QKeySequence& theKeySequence)
53 myConfirmedKeySequenceString = theKeySequence.toString();
54 myKeySequenceLineEdit->setText(myConfirmedKeySequenceString);
55 myPrevKeySequenceString = myConfirmedKeySequenceString;
58 void QtxKeySequenceEdit::setEditedKeySequence(const QKeySequence& theKeySequence)
60 const QString keySequenceString = theKeySequence.toString();
61 myKeySequenceLineEdit->setText(keySequenceString);
62 myPrevKeySequenceString = keySequenceString;
65 QKeySequence QtxKeySequenceEdit::editedKeySequence() const
67 return QKeySequence::fromString(myKeySequenceLineEdit->text());
70 /*! \returns true, if the edited key sequence differs from confirmed one. */
71 bool QtxKeySequenceEdit::isKeySequenceModified() const
73 return QKeySequence(myConfirmedKeySequenceString) != editedKeySequence();
76 /*! \brief Set confirmed key sequence to line editor. */
77 void QtxKeySequenceEdit::restoreKeySequence()
79 myKeySequenceLineEdit->setText(myConfirmedKeySequenceString);
80 myPrevKeySequenceString = myConfirmedKeySequenceString;
84 \brief Gets the key sequence from keys that were pressed
86 \returns a string representation of the key sequence
88 /*static*/ QString QtxKeySequenceEdit::parseEvent(QKeyEvent* e)
90 bool isShiftPressed = e->modifiers() & Qt::ShiftModifier;
91 bool isControlPressed = e->modifiers() & Qt::ControlModifier;
92 bool isAltPressed = e->modifiers() & Qt::AltModifier;
93 bool isMetaPressed = e->modifiers() & Qt::MetaModifier;
94 bool isModifiersPressed = isControlPressed || isAltPressed || isMetaPressed; // Do not treat Shift alone as a modifier!
106 if ((isValidKey(aKey) && isModifiersPressed) || ((aKey >= Qt::Key_F1) && (aKey <= Qt::Key_F12)))
109 return QKeySequence(result).toString();
113 \brief Check if the key event contains a 'valid' key
114 \param theKey the code of the key
115 \returns \c true if the key is 'valid'
117 /*static*/ bool QtxKeySequenceEdit::isValidKey(int theKey)
119 if ( theKey == Qt::Key_Underscore || theKey == Qt::Key_Escape ||
120 ( theKey >= Qt::Key_Backspace && theKey <= Qt::Key_Delete ) ||
121 ( theKey >= Qt::Key_Home && theKey <= Qt::Key_PageDown ) ||
122 ( theKey >= Qt::Key_F1 && theKey <= Qt::Key_F12 ) ||
123 ( theKey >= Qt::Key_Space && theKey <= Qt::Key_Asterisk ) ||
124 ( theKey >= Qt::Key_Comma && theKey <= Qt::Key_Question ) ||
125 ( theKey >= Qt::Key_A && theKey <= Qt::Key_AsciiTilde ) )
130 /*! \brief Called when "Clear" button is clicked. */
131 void QtxKeySequenceEdit::onClear()
133 myKeySequenceLineEdit->setText("");
134 myPrevKeySequenceString = "";
135 emit editingFinished();
138 /*! \brief Called when myKeySequenceLineEdit loses focus. */
139 void QtxKeySequenceEdit::onEditingFinished()
141 if (myKeySequenceLineEdit->text().endsWith("+"))
142 myKeySequenceLineEdit->setText(myPrevKeySequenceString);
144 myPrevKeySequenceString = myKeySequenceLineEdit->text();
145 emit editingFinished();
149 \brief Custom event filter.
150 \param obj event receiver object
152 \returns \c true if further event processing should be stopped
154 bool QtxKeySequenceEdit::eventFilter(QObject* theObject, QEvent* theEvent)
156 if (theObject == myKeySequenceLineEdit) {
157 if (theEvent->type() == QEvent::KeyPress) {
158 QKeyEvent* keyEvent = static_cast<QKeyEvent*>(theEvent);
159 QString text = parseEvent(keyEvent);
160 if (keyEvent->key() == Qt::Key_Delete || keyEvent->key() == Qt::Key_Backspace)
161 myKeySequenceLineEdit->setText("");
163 myKeySequenceLineEdit->setText(text);
165 emit editingStarted();
168 if (theEvent->type() == QEvent::KeyRelease) {
177 \brief Perform internal intialization.
179 void QtxKeySequenceEdit::initialize()
181 static const int PIXMAP_SIZE = 30;
183 QHBoxLayout* base = new QHBoxLayout( this );
187 base->addWidget(myKeySequenceLineEdit = new QLineEdit(this));
188 setFocusProxy(myKeySequenceLineEdit);
190 QToolButton* clearBtn = new QToolButton();
191 auto clearPixmap = QPixmap(":/images/shortcut_disable.svg");
192 clearPixmap.scaled(QSize(PIXMAP_SIZE, PIXMAP_SIZE), Qt::KeepAspectRatio, Qt::SmoothTransformation);
193 clearBtn->setIcon(clearPixmap);
194 clearBtn->setToolTip(tr("Disable shortcut."));
195 base->addWidget(clearBtn);
197 QToolButton* restoreBtn = new QToolButton();
198 auto restorePixmap = QPixmap(":/images/shortcut_restore.svg");
199 restorePixmap.scaled(QSize(PIXMAP_SIZE, PIXMAP_SIZE), Qt::KeepAspectRatio, Qt::SmoothTransformation);
200 restoreBtn->setIcon(restorePixmap);
201 restoreBtn->setToolTip(tr("Restore the currently applied key sequence."));
202 base->addWidget(restoreBtn);
204 myKeySequenceLineEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
205 clearBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
206 restoreBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
208 connect(clearBtn, SIGNAL(clicked()), this, SLOT(onClear()));
209 connect(restoreBtn, SIGNAL(clicked()), this, SIGNAL(restoreFromShortcutMgrClicked()));
210 connect(myKeySequenceLineEdit, SIGNAL(editingFinished()), this, SLOT(onEditingFinished()));
214 /*! \param theParent must not be nullptr. */
215 QtxEditKeySequenceDialog::QtxEditKeySequenceDialog(QtxShortcutTree* theParent)
218 setMinimumWidth(500);
219 setWindowTitle(tr("Change key sequence"));
220 QVBoxLayout* layout = new QVBoxLayout(this);
221 myActionName = new QLabel(this);
222 myActionName->setTextFormat(Qt::RichText);
223 myKeySequenceEdit = new QtxKeySequenceEdit(this);
224 myTextEdit = new QTextEdit(this);
225 layout->addWidget(myActionName);
226 layout->addWidget(myKeySequenceEdit);
227 layout->addWidget(myTextEdit);
228 myActionName->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
229 myKeySequenceEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
230 myTextEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
231 myTextEdit->setReadOnly(true);
232 myTextEdit->setAcceptRichText(true);
233 myTextEdit->setPlaceholderText(tr("No conflicts."));
234 setFocusProxy(myKeySequenceEdit);
236 QHBoxLayout* buttonLayout = new QHBoxLayout(this);
237 layout->addLayout(buttonLayout);
238 QPushButton* confirmButton = new QPushButton(tr("Confirm"), this);
239 confirmButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
240 QPushButton* cancelButton = new QPushButton(tr("Cancel"), this);
241 cancelButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
242 buttonLayout->addStretch();
243 buttonLayout->addWidget(confirmButton);
244 buttonLayout->addWidget(cancelButton);
246 connect(myKeySequenceEdit, SIGNAL(editingStarted()), this, SLOT(onEditingStarted()));
247 connect(myKeySequenceEdit, SIGNAL(editingFinished()), this, SLOT(onEditingFinished()));
248 connect(myKeySequenceEdit, SIGNAL(restoreFromShortcutMgrClicked()), this, SLOT(onRestoreFromShortcutMgr()));
249 connect(confirmButton, SIGNAL(clicked()), this, SLOT(onConfirm()));
250 connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
253 void QtxEditKeySequenceDialog::setModuleAndActionID(const QString& theModuleID, const QString& theInModuleActionID)
255 myModuleID = theModuleID;
256 myInModuleActionID = theInModuleActionID;
259 const QString& QtxEditKeySequenceDialog::moduleID() const { return myModuleID; }
260 const QString& QtxEditKeySequenceDialog::inModuleActionID() const { return myInModuleActionID; }
262 void QtxEditKeySequenceDialog::setModuleAndActionName(const QString& theModuleName, const QString& theActionName)
264 myActionName->setText("<b>" + theModuleName + "</b> " + theActionName);
267 void QtxEditKeySequenceDialog::setConfirmedKeySequence(const QKeySequence& theSequence)
269 myKeySequenceEdit->setConfirmedKeySequence(theSequence);
272 QKeySequence QtxEditKeySequenceDialog::editedKeySequence() const
274 return myKeySequenceEdit->editedKeySequence();
277 int QtxEditKeySequenceDialog::exec()
279 myKeySequenceEdit->setFocus(Qt::ActiveWindowFocusReason);
280 return QDialog::exec();
283 void QtxEditKeySequenceDialog::onEditingStarted()
285 myTextEdit->setEnabled(false);
288 void QtxEditKeySequenceDialog::onEditingFinished()
290 updateConflictsMessage();
293 void QtxEditKeySequenceDialog::onRestoreFromShortcutMgr()
295 const auto shortcutMgr = SUIT_ShortcutMgr::get();
296 myKeySequenceEdit->setEditedKeySequence(shortcutMgr->getKeySequence(myModuleID, myInModuleActionID));
297 updateConflictsMessage();
300 /*! Updates message with list of actions, whose shortcuts will be disabled on Confirm. */
301 void QtxEditKeySequenceDialog::updateConflictsMessage()
303 myTextEdit->setEnabled(true);
304 QTextDocument* doc = myTextEdit->document();
306 doc = new QTextDocument(myTextEdit);
307 myTextEdit->setDocument(doc);
310 if (!myKeySequenceEdit->isKeySequenceModified()) {
315 const QKeySequence newKeySequence = editedKeySequence();
317 const auto shortcutTree = static_cast<QtxShortcutTree*>(parentWidget());
318 /** {moduleID, inModuleActionID}[] */
319 std::set<std::pair<QString, QString>> conflicts = shortcutTree->shortcutContainer()->getConflicts(myModuleID, myInModuleActionID, newKeySequence);
320 if (!conflicts.empty()) {
321 const auto shortcutMgr = SUIT_ShortcutMgr::get();
323 QString report = "<b>" + tr("These shortcuts will be disabled on confirm:") + "</b>";
326 for (const auto& conflict : conflicts) {
327 const QString conflictingModuleName = shortcutMgr->getModuleName(conflict.first);
328 const QString conflictingActionName = shortcutMgr->getActionName(conflict.first, conflict.second);
329 report += "<li><b>" + conflictingModuleName + "</b> " + conflictingActionName + "</li>";
333 doc->setHtml(report);
335 else /* if no conflicts */ {
340 void QtxEditKeySequenceDialog::onConfirm()
342 if (myKeySequenceEdit->isKeySequenceModified())
349 /*! \param theContainer Share the same container between several trees,
350 to edit them synchronously even without exchange of changes with SUIT_ShortcutMgr.
351 Pass nullptr to create non-synchronized tree. */
352 QtxShortcutTree::QtxShortcutTree(
353 std::shared_ptr<SUIT_ShortcutContainer> theContainer,
355 ) : QTreeWidget(theParent),
356 myShortcutContainer(theContainer ? theContainer : std::shared_ptr<SUIT_ShortcutContainer>(new SUIT_ShortcutContainer()))
359 setSelectionMode(QAbstractItemView::SingleSelection);
360 setColumnWidth(0, COLUMN_SIZE);
361 setSortingEnabled(false); // Items are sorted in the same way, as in ShortcutContainer.
362 headerItem()->setHidden(true);
363 setExpandsOnDoubleClick(false); // Open shortcut editor on double click instead.
364 setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
365 setToolTip(tr("Double click to edit key sequence."));
366 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
367 myEditDialog = new QtxEditKeySequenceDialog(this);
369 this->installEventFilter(this);
370 connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(onItemDoubleClicked(QTreeWidgetItem*, int)));
372 QtxShortcutTree::instances[myShortcutContainer.get()].emplace(this);
375 QtxShortcutTree::~QtxShortcutTree()
377 QtxShortcutTree::instances[myShortcutContainer.get()].erase(this);
378 if (QtxShortcutTree::instances[myShortcutContainer.get()].empty())
379 QtxShortcutTree::instances.erase(myShortcutContainer.get());
382 /*! \brief Copies shortcuts from ShortcutMgr. (Re)displays shortcuts of myModuleIDs. */
383 void QtxShortcutTree::setShortcutsFromManager()
385 const auto shortcutMgr = SUIT_ShortcutMgr::get();
386 *myShortcutContainer = shortcutMgr->getShortcutContainer();
387 // nb! ShortcutMgr never removes shortcuts from its container, only disables.
389 updateItems(false /*theHighlightModified*/, true /*theUpdateSyncTrees*/);
392 /*! \brief Copies shortcuts from resources, user files are not accounted. (Re)displays shortcuts of myModuleIDs. */
393 void QtxShortcutTree::setDefaultShortcuts()
395 SUIT_ShortcutContainer defaultShortcuts;
396 SUIT_ShortcutMgr::fillContainerFromPreferences(defaultShortcuts, true /*theDefaultOnly*/);
398 myShortcutContainer->merge(defaultShortcuts, true /*theOverride*/, true /*theTreatAbsentIncomingAsDisabled*/);
399 // nb! SUIT_ShortcutContainer never erases shortcuts, only disables.
401 updateItems(true /*theHighlightModified*/, true /*theUpdateSyncTrees*/);
404 /*! \brief Applies pending changes to ShortcutMgr. Updates other instances of QtxShortcutTree. */
405 void QtxShortcutTree::applyChangesToShortcutMgr()
407 const auto mgr = SUIT_ShortcutMgr::get();
408 mgr->mergeShortcutContainer(*myShortcutContainer);
410 // Update non-synchronized with this instances.
411 for (const auto& containerAndSyncTrees : QtxShortcutTree::instances) {
412 if (containerAndSyncTrees.first == myShortcutContainer.get())
415 const std::set<QtxShortcutTree*>& syncTrees = containerAndSyncTrees.second;
416 const auto itFirstSyncTree = syncTrees.begin();
417 if (itFirstSyncTree == syncTrees.end())
420 (*itFirstSyncTree)->setShortcutsFromManager();
421 const auto editDialog = (*itFirstSyncTree)->myEditDialog;
422 editDialog->setConfirmedKeySequence(mgr->getShortcutContainer().getKeySequence(editDialog->moduleID(), editDialog->inModuleActionID()));
423 editDialog->updateConflictsMessage();
427 std::shared_ptr<const SUIT_ShortcutContainer> QtxShortcutTree::shortcutContainer() const
429 return myShortcutContainer;
432 /*! \param If theUpdateSyncTrees, trees sharing the same shortcut container are updated. */
433 void QtxShortcutTree::updateItems(bool theHighlightModified, bool theUpdateSyncTrees)
435 const auto shortcutMgr = SUIT_ShortcutMgr::get();
437 for (const QString& moduleID : myModuleIDs) {
438 const auto& moduleShortcuts = myShortcutContainer->getModuleShortcutsInversed(moduleID);
439 if (moduleShortcuts.empty()) {
440 // Do not display empty module.
441 const auto moduleItemAndIdx = findModuleFolderItem(moduleID);
442 if (moduleItemAndIdx.second >= 0)
443 takeTopLevelItem(moduleItemAndIdx.second);
448 const auto moduleItemAndIdx = findModuleFolderItem(moduleID);
449 QtxShortcutTreeItem* moduleItem = moduleItemAndIdx.first;
451 moduleItem = QtxShortcutTreeItem::createFolderItem(moduleID);
452 moduleItem->setName(shortcutMgr->getModuleName(moduleID));
453 addTopLevelItem(moduleItem);
454 moduleItem->setFlags(Qt::ItemIsEnabled);
456 for (const auto& shortcut : moduleShortcuts) {
457 const QString& inModuleActionID = shortcut.first;
458 const QKeySequence& keySequence = shortcut.second;
459 const QString keySequenceString = keySequence.toString();
461 auto shortcutItem = QtxShortcutTreeItem::createShortcutItem(moduleID, inModuleActionID);
463 ShCutDbg("QtxShortcutTree can't create child item for action ID = \"" + SUIT_ShortcutMgr::makeActionID(moduleID, inModuleActionID) + "\".");
467 shortcutItem->setName(shortcutMgr->getActionName(moduleID, inModuleActionID));
468 shortcutItem->setKeySequence(keySequenceString);
470 if (theHighlightModified) {
471 const QKeySequence& appliedKeySequence = SUIT_ShortcutMgr::get()->getKeySequence(moduleID, inModuleActionID);
472 shortcutItem->highlightKeySequenceAsModified(keySequence != appliedKeySequence);
475 moduleItem->addChild(shortcutItem);
478 moduleItem->setExpanded(true); // Make tree expanded on first show.
480 else /* if the tree has the module-item */ {
481 for (int childIdx = 0; childIdx < moduleItem->childCount(); childIdx++) {
482 // Update exisiting items of a module.
483 QtxShortcutTreeItem* const childItem = static_cast<QtxShortcutTreeItem*>(moduleItem->child(childIdx));
484 const auto itShortcut = moduleShortcuts.find(childItem->myInModuleActionID);
485 if (itShortcut == moduleShortcuts.end()) {
486 // Shortcut of the item has been removed from myShortcutContainer - impossible.
489 const QKeySequence& newKeySequence = itShortcut->second;
490 const QString newKeySequenceString = newKeySequence.toString();
491 if (childItem->keySequence() != newKeySequenceString)
492 childItem->setKeySequence(newKeySequenceString);
494 if (theHighlightModified) {
495 const QKeySequence& appliedKeySequence = SUIT_ShortcutMgr::get()->getKeySequence(moduleID, childItem->myInModuleActionID);
496 childItem->highlightKeySequenceAsModified(newKeySequence != appliedKeySequence);
499 childItem->highlightKeySequenceAsModified(false);
502 // Add new items if myShortcutContainer acquired new shortcuts, which may happen if a developer forgot
503 // to add shortcuts for registered actions to resource files.
504 if (moduleItem->childCount() < moduleShortcuts.size()) {
505 // Module shortcuts and tree items must be ordered with the same comparator. Now it is std::less(inModuleActionID_A, inModuleActionID_B).
506 std::set<QString> actionIDsOfItems;
507 for (int childIdx = 0; childIdx < moduleItem->childCount(); childIdx++) {
508 QtxShortcutTreeItem* const childItem = static_cast<QtxShortcutTreeItem*>(moduleItem->child(childIdx));
509 actionIDsOfItems.emplace(childItem->myInModuleActionID);
512 for (const auto& shortcut : moduleShortcuts) {
513 const QString& inModuleActionID = shortcut.first;
514 const QKeySequence& keySequence = shortcut.second;
516 auto itNewActionID = actionIDsOfItems.emplace(inModuleActionID).first;
518 // Replace this with std::distance if C++ >= 17.
519 auto it = actionIDsOfItems.begin();
520 while (it != itNewActionID) {
525 const auto shortcutItem = QtxShortcutTreeItem::createShortcutItem(moduleID, inModuleActionID);
527 ShCutDbg("QtxShortcutTree can't create child item for action ID = \"" + SUIT_ShortcutMgr::makeActionID(moduleID, inModuleActionID) + "\".");
531 shortcutItem->setName(shortcutMgr->getActionName(moduleID, inModuleActionID));
532 shortcutItem->setKeySequence(keySequence.toString());
534 if (theHighlightModified) {
535 const QKeySequence& appliedKeySequence = SUIT_ShortcutMgr::get()->getKeySequence(moduleID, inModuleActionID);
536 shortcutItem->highlightKeySequenceAsModified(keySequence != appliedKeySequence);
539 moduleItem->insertChild(newItemIdx, shortcutItem);
545 if (theUpdateSyncTrees) {
546 const std::set<QtxShortcutTree*>& syncTrees = QtxShortcutTree::instances[myShortcutContainer.get()];
547 for (const auto syncTree: syncTrees) {
548 if (syncTree == this)
551 syncTree->updateItems(theHighlightModified, false /*theUpdateSyncTrees*/);
552 const auto editDialog = syncTree->myEditDialog;
553 editDialog->setConfirmedKeySequence(myShortcutContainer->getKeySequence(editDialog->moduleID(), editDialog->inModuleActionID()));
554 editDialog->updateConflictsMessage();
559 /*! \returns Pointer and index of top-level item.
560 If the tree does not contain an item with theModuleID, returns {nullptr, -1}. */
561 std::pair<QtxShortcutTreeItem*, int> QtxShortcutTree::findModuleFolderItem(const QString& theModuleID) const
563 for (int moduleIdx = 0; moduleIdx < topLevelItemCount(); moduleIdx++) {
564 QtxShortcutTreeItem* moduleItem = static_cast<QtxShortcutTreeItem*>(topLevelItem(moduleIdx));
565 if (moduleItem->myModuleID == theModuleID)
566 return std::pair<QtxShortcutTreeItem*, int>(moduleItem, moduleIdx);
568 return std::pair<QtxShortcutTreeItem*, int>(nullptr, -1);
571 void QtxShortcutTree::onItemDoubleClicked(QTreeWidgetItem* theItem, int theColIdx)
573 QtxShortcutTreeItem* const item = static_cast<QtxShortcutTreeItem*>(theItem);
574 // Do not react if folder-item is clicked.
575 if (item->isFolder())
578 myEditDialog->setModuleAndActionID(item->myModuleID, item->myInModuleActionID);
579 myEditDialog->setModuleAndActionName(static_cast<QtxShortcutTreeItem*>(item->parent())->name(), item->name());
580 myEditDialog->setConfirmedKeySequence(QKeySequence::fromString(item->keySequence()));
581 myEditDialog->updateConflictsMessage();
582 const bool somethingChanged = myEditDialog->exec() == QDialog::Accepted;
584 if (!somethingChanged)
587 const QKeySequence newKeySequence = myEditDialog->editedKeySequence();
589 /** { moduleID, inModuleActionID }[] */
590 std::set<std::pair<QString, QString>> disabledActionIDs = myShortcutContainer->setShortcut(item->myModuleID, item->myInModuleActionID, newKeySequence, true /*override*/);
592 /** { moduleID, {inModuleActionID, keySequence}[] }[] */
593 std::map<QString, std::map<QString, QString>> changes;
594 changes[item->myModuleID][item->myInModuleActionID] = newKeySequence.toString();
595 for (const auto moduleAndActionID : disabledActionIDs) {
596 changes[moduleAndActionID.first][moduleAndActionID.second] = QString();
599 // Set new key sequences to shortcut items.
600 for (const auto& moduleIDAndChanges : changes) {
601 const QString& moduleID = moduleIDAndChanges.first;
603 const auto moduleItemAndIdx = findModuleFolderItem(moduleID);
604 const auto moduleItem = moduleItemAndIdx.first;
608 /** {inModuleActionID, newKeySequence}[] */
609 const std::map<QString, QString>& moduleChanges = moduleIDAndChanges.second;
611 // Go through module' shortcut items, and highlight those, whose key sequences differ from applied key sequences.
612 for (int childIdx = 0; childIdx < moduleItem->childCount(); childIdx++) {
613 QtxShortcutTreeItem* const childItem = static_cast<QtxShortcutTreeItem*>(moduleItem->child(childIdx));
614 const auto itChange = moduleChanges.find(childItem->myInModuleActionID);
615 if (itChange == moduleChanges.end()) {
616 // The shortcut has not been changed.
620 childItem->setKeySequence(itChange->second);
622 const QKeySequence& appliedKeySequence = SUIT_ShortcutMgr::get()->getKeySequence(moduleID, childItem->myInModuleActionID);
623 childItem->highlightKeySequenceAsModified(QKeySequence::fromString(itChange->second) != appliedKeySequence);
628 /*static*/ std::map<SUIT_ShortcutContainer*, std::set<QtxShortcutTree*>> QtxShortcutTree::instances =
629 std::map<SUIT_ShortcutContainer*, std::set<QtxShortcutTree*>>();
632 QtxShortcutTreeItem::QtxShortcutTreeItem(const QString& theModuleID, const QString& theInModuleActionID)
633 : QTreeWidgetItem(), myModuleID(theModuleID), myInModuleActionID(theInModuleActionID)
636 /*static*/ QtxShortcutTreeItem* QtxShortcutTreeItem::createFolderItem(const QString& theModuleID)
638 auto item = new QtxShortcutTreeItem(theModuleID, QString());
640 QFont font = item->font(ElementIdx::Name);
642 item->setFont(ElementIdx::Name, font);
647 /*! \returns nullptr if theInModuleActionID is empty. */
648 /*static*/ QtxShortcutTreeItem* QtxShortcutTreeItem::createShortcutItem(const QString& theModuleID, const QString& theInModuleActionID)
650 if (theInModuleActionID.isEmpty()) {
651 ShCutDbg("QtxShortcutTreeItem: attempt to create item with empty action ID.");
655 return new QtxShortcutTreeItem(theModuleID, theInModuleActionID);
658 bool QtxShortcutTreeItem::isFolder() const
660 return myInModuleActionID.isEmpty();
663 /*! \brief Highlights text at ElementIdx::KeySequence. */
664 void QtxShortcutTreeItem::highlightKeySequenceAsModified(bool theHighlight)
666 static const QBrush bgHighlitingBrush = QBrush(Qt::darkGreen);
667 static const QBrush fgHighlitingBrush = QBrush(Qt::white);
668 static const QBrush noBrush = QBrush();
670 setBackground(ElementIdx::KeySequence, theHighlight ? bgHighlitingBrush : noBrush);
671 setForeground(ElementIdx::KeySequence, theHighlight ? fgHighlitingBrush : noBrush);
674 void QtxShortcutTreeItem::setName(const QString& theName)
676 setText(ElementIdx::Name, theName);
679 QString QtxShortcutTreeItem::name() const
681 return text(ElementIdx::Name);
684 void QtxShortcutTreeItem::setKeySequence(const QString& theKeySequence)
686 setText(ElementIdx::KeySequence, theKeySequence);
689 QString QtxShortcutTreeItem::keySequence() const
691 return text(ElementIdx::KeySequence);