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"
27 #include <QToolButton>
30 #include <QTableWidgetItem>
32 #include <QMessageBox>
33 #include <QPushButton>
36 #include <QHeaderView>
39 #include <QKeySequence>
42 #define COLUMN_SIZE 500
45 QtxKeySequenceEdit::QtxKeySequenceEdit(QWidget* parent)
49 myKeySequenceLineEdit->installEventFilter(this);
52 /*! \brief Set a key sequence to edit. */
53 void QtxKeySequenceEdit::setConfirmedKeySequence(const QKeySequence& theKeySequence)
55 myConfirmedKeySequenceString = theKeySequence.toString();
56 myKeySequenceLineEdit->setText(myConfirmedKeySequenceString);
57 myPrevKeySequenceString = myConfirmedKeySequenceString;
60 void QtxKeySequenceEdit::setEditedKeySequence(const QKeySequence& theKeySequence)
62 const QString keySequenceString = theKeySequence.toString();
63 myKeySequenceLineEdit->setText(keySequenceString);
64 myPrevKeySequenceString = keySequenceString;
67 QKeySequence QtxKeySequenceEdit::editedKeySequence() const
69 return QKeySequence::fromString(myKeySequenceLineEdit->text());
72 /*! \returns true, if the edited key sequence differs from confirmed one. */
73 bool QtxKeySequenceEdit::isKeySequenceModified() const
75 return QKeySequence(myConfirmedKeySequenceString) != editedKeySequence();
78 /*! \brief Set confirmed key sequence to line editor. */
79 void QtxKeySequenceEdit::restoreKeySequence()
81 myKeySequenceLineEdit->setText(myConfirmedKeySequenceString);
82 myPrevKeySequenceString = myConfirmedKeySequenceString;
86 \brief Gets the key sequence from keys that were pressed
88 \returns a string representation of the key sequence
90 /*static*/ QString QtxKeySequenceEdit::parseEvent(QKeyEvent* e)
92 bool isShiftPressed = e->modifiers() & Qt::ShiftModifier;
93 bool isControlPressed = e->modifiers() & Qt::ControlModifier;
94 bool isAltPressed = e->modifiers() & Qt::AltModifier;
95 bool isMetaPressed = e->modifiers() & Qt::MetaModifier;
96 bool isModifiersPressed = isControlPressed || isAltPressed || isMetaPressed; // Do not treat Shift alone as a modifier!
108 if ((isValidKey(aKey) && isModifiersPressed) || ((aKey >= Qt::Key_F1) && (aKey <= Qt::Key_F12)))
111 return QKeySequence(result).toString();
115 \brief Check if the key event contains a 'valid' key
116 \param theKey the code of the key
117 \returns \c true if the key is 'valid'
119 /*static*/ bool QtxKeySequenceEdit::isValidKey(int theKey)
121 if ( theKey == Qt::Key_Underscore || theKey == Qt::Key_Escape ||
122 ( theKey >= Qt::Key_Backspace && theKey <= Qt::Key_Delete ) ||
123 ( theKey >= Qt::Key_Home && theKey <= Qt::Key_PageDown ) ||
124 ( theKey >= Qt::Key_F1 && theKey <= Qt::Key_F12 ) ||
125 ( theKey >= Qt::Key_Space && theKey <= Qt::Key_Asterisk ) ||
126 ( theKey >= Qt::Key_Comma && theKey <= Qt::Key_Question ) ||
127 ( theKey >= Qt::Key_A && theKey <= Qt::Key_AsciiTilde ) )
132 /*! \brief Called when "Clear" button is clicked. */
133 void QtxKeySequenceEdit::onClear()
135 myKeySequenceLineEdit->setText("");
136 myPrevKeySequenceString = "";
137 emit editingFinished();
140 /*! \brief Called when myKeySequenceLineEdit loses focus. */
141 void QtxKeySequenceEdit::onEditingFinished()
143 if (myKeySequenceLineEdit->text().endsWith("+"))
144 myKeySequenceLineEdit->setText(myPrevKeySequenceString);
146 myPrevKeySequenceString = myKeySequenceLineEdit->text();
147 emit editingFinished();
151 \brief Custom event filter.
152 \param obj event receiver object
154 \returns \c true if further event processing should be stopped
156 bool QtxKeySequenceEdit::eventFilter(QObject* theObject, QEvent* theEvent)
158 if (theObject == myKeySequenceLineEdit) {
159 if (theEvent->type() == QEvent::KeyPress) {
160 QKeyEvent* keyEvent = static_cast<QKeyEvent*>(theEvent);
161 QString text = parseEvent(keyEvent);
162 if (keyEvent->key() == Qt::Key_Delete || keyEvent->key() == Qt::Key_Backspace)
163 myKeySequenceLineEdit->setText("");
165 myKeySequenceLineEdit->setText(text);
167 emit editingStarted();
170 if (theEvent->type() == QEvent::KeyRelease) {
179 \brief Perform internal intialization.
181 void QtxKeySequenceEdit::initialize()
183 static const int PIXMAP_SIZE = 30;
185 QHBoxLayout* base = new QHBoxLayout( this );
189 base->addWidget(myKeySequenceLineEdit = new QLineEdit(this));
190 setFocusProxy(myKeySequenceLineEdit);
192 QToolButton* clearBtn = new QToolButton();
193 auto clearPixmap = QPixmap(":/images/shortcut_disable.svg");
194 clearPixmap.scaled(QSize(PIXMAP_SIZE, PIXMAP_SIZE), Qt::KeepAspectRatio, Qt::SmoothTransformation);
195 clearBtn->setIcon(clearPixmap);
196 clearBtn->setToolTip(tr("Disable shortcut."));
197 base->addWidget(clearBtn);
199 QToolButton* restoreBtn = new QToolButton();
200 auto restorePixmap = QPixmap(":/images/shortcut_restore.svg");
201 restorePixmap.scaled(QSize(PIXMAP_SIZE, PIXMAP_SIZE), Qt::KeepAspectRatio, Qt::SmoothTransformation);
202 restoreBtn->setIcon(restorePixmap);
203 restoreBtn->setToolTip(tr("Restore the currently applied key sequence."));
204 base->addWidget(restoreBtn);
206 myKeySequenceLineEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
207 clearBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
208 restoreBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
210 connect(clearBtn, SIGNAL(clicked()), this, SLOT(onClear()));
211 connect(restoreBtn, SIGNAL(clicked()), this, SIGNAL(restoreFromShortcutMgrClicked()));
212 connect(myKeySequenceLineEdit, SIGNAL(editingFinished()), this, SLOT(onEditingFinished()));
216 /*! \param theParent must not be nullptr. */
217 QtxEditKeySequenceDialog::QtxEditKeySequenceDialog(QtxShortcutTree* theParent)
220 setMinimumWidth(500);
221 setWindowTitle(tr("Change key sequence"));
222 QVBoxLayout* layout = new QVBoxLayout(this);
223 myActionName = new QLabel(this);
224 myActionName->setTextFormat(Qt::RichText);
225 myKeySequenceEdit = new QtxKeySequenceEdit(this);
226 myTextEdit = new QTextEdit(this);
227 layout->addWidget(myActionName);
228 layout->addWidget(myKeySequenceEdit);
229 layout->addWidget(myTextEdit);
230 myActionName->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
231 myKeySequenceEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
232 myTextEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
233 myTextEdit->setReadOnly(true);
234 myTextEdit->setAcceptRichText(true);
235 myTextEdit->setPlaceholderText(tr("No conflicts."));
236 setFocusProxy(myKeySequenceEdit);
238 QHBoxLayout* buttonLayout = new QHBoxLayout(this);
239 layout->addLayout(buttonLayout);
240 QPushButton* confirmButton = new QPushButton(tr("Confirm"), this);
241 confirmButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
242 QPushButton* cancelButton = new QPushButton(tr("Cancel"), this);
243 cancelButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
244 buttonLayout->addStretch();
245 buttonLayout->addWidget(confirmButton);
246 buttonLayout->addWidget(cancelButton);
248 connect(myKeySequenceEdit, SIGNAL(editingStarted()), this, SLOT(onEditingStarted()));
249 connect(myKeySequenceEdit, SIGNAL(editingFinished()), this, SLOT(onEditingFinished()));
250 connect(myKeySequenceEdit, SIGNAL(restoreFromShortcutMgrClicked()), this, SLOT(onRestoreFromShortcutMgr()));
251 connect(confirmButton, SIGNAL(clicked()), this, SLOT(onConfirm()));
252 connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
255 void QtxEditKeySequenceDialog::setModuleAndActionID(const QString& theModuleID, const QString& theInModuleActionID)
257 myModuleID = theModuleID;
258 myInModuleActionID = theInModuleActionID;
261 const QString& QtxEditKeySequenceDialog::moduleID() const { return myModuleID; }
262 const QString& QtxEditKeySequenceDialog::inModuleActionID() const { return myInModuleActionID; }
264 void QtxEditKeySequenceDialog::setModuleAndActionName(const QString& theModuleName, const QString& theActionName)
266 myActionName->setText("<b>" + theModuleName + "</b> " + theActionName);
269 void QtxEditKeySequenceDialog::setConfirmedKeySequence(const QKeySequence& theSequence)
271 myKeySequenceEdit->setConfirmedKeySequence(theSequence);
274 QKeySequence QtxEditKeySequenceDialog::editedKeySequence() const
276 return myKeySequenceEdit->editedKeySequence();
279 int QtxEditKeySequenceDialog::exec()
281 myKeySequenceEdit->setFocus(Qt::ActiveWindowFocusReason);
282 return QDialog::exec();
285 void QtxEditKeySequenceDialog::onEditingStarted()
287 myTextEdit->setEnabled(false);
290 void QtxEditKeySequenceDialog::onEditingFinished()
292 updateConflictsMessage();
295 void QtxEditKeySequenceDialog::onRestoreFromShortcutMgr()
297 const auto shortcutMgr = SUIT_ShortcutMgr::get();
298 myKeySequenceEdit->setEditedKeySequence(shortcutMgr->getKeySequence(myModuleID, myInModuleActionID));
299 updateConflictsMessage();
302 /*! Updates message with list of actions, whose shortcuts will be disabled on Confirm. */
303 void QtxEditKeySequenceDialog::updateConflictsMessage()
305 myTextEdit->setEnabled(true);
306 QTextDocument* doc = myTextEdit->document();
308 doc = new QTextDocument(myTextEdit);
309 myTextEdit->setDocument(doc);
312 if (!myKeySequenceEdit->isKeySequenceModified()) {
317 const QKeySequence newKeySequence = editedKeySequence();
319 const auto shortcutTree = static_cast<QtxShortcutTree*>(parentWidget());
320 /** {moduleID, inModuleActionID}[] */
321 std::set<std::pair<QString, QString>> conflicts = shortcutTree->shortcutContainer()->getConflicts(myModuleID, myInModuleActionID, newKeySequence);
322 if (!conflicts.empty()) {
323 const auto shortcutMgr = SUIT_ShortcutMgr::get();
325 QString report = "<b>" + tr("These shortcuts will be disabled on confirm:") + "</b>";
328 for (const auto& conflict : conflicts) {
329 const QString conflictingModuleName = shortcutMgr->getModuleName(conflict.first);
330 const QString conflictingActionName = shortcutMgr->getActionName(conflict.first, conflict.second);
331 report += "<li><b>" + conflictingModuleName + "</b> " + conflictingActionName + "</li>";
335 doc->setHtml(report);
337 else /* if no conflicts */ {
342 void QtxEditKeySequenceDialog::onConfirm()
344 if (myKeySequenceEdit->isKeySequenceModified())
351 /*! \param theContainer Share the same container between several trees,
352 to edit them synchronously even without exchange of changes with SUIT_ShortcutMgr.
353 Pass nullptr to create non-synchronized tree. */
354 QtxShortcutTree::QtxShortcutTree(
355 std::shared_ptr<SUIT_ShortcutContainer> theContainer,
357 ) : QTreeWidget(theParent),
358 myShortcutContainer(theContainer ? theContainer : std::shared_ptr<SUIT_ShortcutContainer>(new SUIT_ShortcutContainer()))
361 setSelectionMode(QAbstractItemView::SingleSelection);
362 setColumnWidth(0, COLUMN_SIZE);
363 setSortingEnabled(false); // Items are sorted in the same way, as in ShortcutContainer.
364 header()->setSectionResizeMode(QHeaderView::Interactive);
366 QMap<int, QString> labelMap;
367 labelMap[QtxShortcutTree::ElementIdx::Name] = tr("Action");
368 labelMap[QtxShortcutTree::ElementIdx::KeySequence] = tr("Key sequence");
369 setHeaderLabels(labelMap.values());
371 setExpandsOnDoubleClick(false); // Open shortcut editor on double click instead.
372 setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
373 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
374 myEditDialog = new QtxEditKeySequenceDialog(this);
376 this->installEventFilter(this);
377 connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(onItemDoubleClicked(QTreeWidgetItem*, int)));
379 QtxShortcutTree::instances[myShortcutContainer.get()].emplace(this);
382 QtxShortcutTree::~QtxShortcutTree()
384 QtxShortcutTree::instances[myShortcutContainer.get()].erase(this);
385 if (QtxShortcutTree::instances[myShortcutContainer.get()].empty())
386 QtxShortcutTree::instances.erase(myShortcutContainer.get());
389 /*! \brief Copies shortcuts from ShortcutMgr. (Re)displays shortcuts of myModuleIDs. */
390 void QtxShortcutTree::setShortcutsFromManager()
392 const auto shortcutMgr = SUIT_ShortcutMgr::get();
393 *myShortcutContainer = shortcutMgr->getShortcutContainer();
394 // nb! ShortcutMgr never removes shortcuts from its container, only disables.
396 updateItems(false /*theHighlightModified*/, true /*theUpdateSyncTrees*/);
399 /*! \brief Copies shortcuts from resources, user files are not accounted. (Re)displays shortcuts of myModuleIDs. */
400 void QtxShortcutTree::setDefaultShortcuts()
402 SUIT_ShortcutContainer defaultShortcuts;
403 SUIT_ShortcutMgr::fillContainerFromPreferences(defaultShortcuts, true /*theDefaultOnly*/);
405 myShortcutContainer->merge(defaultShortcuts, true /*theOverride*/, true /*theTreatAbsentIncomingAsDisabled*/);
406 // nb! SUIT_ShortcutContainer never erases shortcuts, only disables.
408 updateItems(true /*theHighlightModified*/, true /*theUpdateSyncTrees*/);
411 /*! \brief Applies pending changes to ShortcutMgr. Updates other instances of QtxShortcutTree. */
412 void QtxShortcutTree::applyChangesToShortcutMgr()
414 const auto mgr = SUIT_ShortcutMgr::get();
415 mgr->mergeShortcutContainer(*myShortcutContainer);
417 // Update non-synchronized with this instances.
418 for (const auto& containerAndSyncTrees : QtxShortcutTree::instances) {
419 if (containerAndSyncTrees.first == myShortcutContainer.get())
422 const std::set<QtxShortcutTree*>& syncTrees = containerAndSyncTrees.second;
423 const auto itFirstSyncTree = syncTrees.begin();
424 if (itFirstSyncTree == syncTrees.end())
427 (*itFirstSyncTree)->setShortcutsFromManager();
428 const auto editDialog = (*itFirstSyncTree)->myEditDialog;
429 editDialog->setConfirmedKeySequence(mgr->getShortcutContainer().getKeySequence(editDialog->moduleID(), editDialog->inModuleActionID()));
430 editDialog->updateConflictsMessage();
434 std::shared_ptr<const SUIT_ShortcutContainer> QtxShortcutTree::shortcutContainer() const
436 return myShortcutContainer;
439 /*! \param If theUpdateSyncTrees, trees sharing the same shortcut container are updated. */
440 void QtxShortcutTree::updateItems(bool theHighlightModified, bool theUpdateSyncTrees)
442 const auto shortcutMgr = SUIT_ShortcutMgr::get();
444 for (const QString& moduleID : myModuleIDs) {
445 const auto& moduleShortcuts = myShortcutContainer->getModuleShortcutsInversed(moduleID);
446 if (moduleShortcuts.empty()) {
447 // Do not display empty module.
448 const auto moduleItemAndIdx = findModuleFolderItem(moduleID);
449 if (moduleItemAndIdx.second >= 0)
450 takeTopLevelItem(moduleItemAndIdx.second);
455 const auto moduleItemAndIdx = findModuleFolderItem(moduleID);
456 QtxShortcutTreeItem* moduleItem = moduleItemAndIdx.first;
458 moduleItem = QtxShortcutTreeItem::createFolderItem(moduleID);
459 moduleItem->setName(shortcutMgr->getModuleName(moduleID));
460 addTopLevelItem(moduleItem);
461 moduleItem->setFlags(Qt::ItemIsEnabled);
463 for (const auto& shortcut : moduleShortcuts) {
464 const QString& inModuleActionID = shortcut.first;
465 const QKeySequence& keySequence = shortcut.second;
466 const QString keySequenceString = keySequence.toString();
468 auto shortcutItem = QtxShortcutTreeItem::createShortcutItem(moduleID, inModuleActionID);
470 ShCutDbg("QtxShortcutTree can't create child item for action ID = \"" + SUIT_ShortcutMgr::makeActionID(moduleID, inModuleActionID) + "\".");
474 shortcutItem->setName(shortcutMgr->getActionName(moduleID, inModuleActionID));
475 shortcutItem->setKeySequence(keySequenceString);
477 if (theHighlightModified) {
478 const QKeySequence& appliedKeySequence = SUIT_ShortcutMgr::get()->getKeySequence(moduleID, inModuleActionID);
479 shortcutItem->highlightKeySequenceAsModified(keySequence != appliedKeySequence);
482 moduleItem->addChild(shortcutItem);
485 moduleItem->setExpanded(true); // Make tree expanded on first show.
487 else /* if the tree has the module-item */ {
488 for (int childIdx = 0; childIdx < moduleItem->childCount(); childIdx++) {
489 // Update exisiting items of a module.
490 QtxShortcutTreeItem* const childItem = static_cast<QtxShortcutTreeItem*>(moduleItem->child(childIdx));
491 const auto itShortcut = moduleShortcuts.find(childItem->myInModuleActionID);
492 if (itShortcut == moduleShortcuts.end()) {
493 // Shortcut of the item has been removed from myShortcutContainer - impossible.
496 const QKeySequence& newKeySequence = itShortcut->second;
497 const QString newKeySequenceString = newKeySequence.toString();
498 if (childItem->keySequence() != newKeySequenceString)
499 childItem->setKeySequence(newKeySequenceString);
501 if (theHighlightModified) {
502 const QKeySequence& appliedKeySequence = SUIT_ShortcutMgr::get()->getKeySequence(moduleID, childItem->myInModuleActionID);
503 childItem->highlightKeySequenceAsModified(newKeySequence != appliedKeySequence);
506 childItem->highlightKeySequenceAsModified(false);
509 // Add new items if myShortcutContainer acquired new shortcuts, which may happen if a developer forgot
510 // to add shortcuts for registered actions to resource files.
511 if (moduleItem->childCount() < moduleShortcuts.size()) {
512 // Module shortcuts and tree items must be ordered with the same comparator. Now it is std::less(inModuleActionID_A, inModuleActionID_B).
513 std::set<QString> actionIDsOfItems;
514 for (int childIdx = 0; childIdx < moduleItem->childCount(); childIdx++) {
515 QtxShortcutTreeItem* const childItem = static_cast<QtxShortcutTreeItem*>(moduleItem->child(childIdx));
516 actionIDsOfItems.emplace(childItem->myInModuleActionID);
519 for (const auto& shortcut : moduleShortcuts) {
520 const QString& inModuleActionID = shortcut.first;
521 const QKeySequence& keySequence = shortcut.second;
523 auto itNewActionID = actionIDsOfItems.emplace(inModuleActionID).first;
525 // Replace this with std::distance if C++ >= 17.
526 auto it = actionIDsOfItems.begin();
527 while (it != itNewActionID) {
532 const auto shortcutItem = QtxShortcutTreeItem::createShortcutItem(moduleID, inModuleActionID);
534 ShCutDbg("QtxShortcutTree can't create child item for action ID = \"" + SUIT_ShortcutMgr::makeActionID(moduleID, inModuleActionID) + "\".");
538 shortcutItem->setName(shortcutMgr->getActionName(moduleID, inModuleActionID));
539 shortcutItem->setKeySequence(keySequence.toString());
541 if (theHighlightModified) {
542 const QKeySequence& appliedKeySequence = SUIT_ShortcutMgr::get()->getKeySequence(moduleID, inModuleActionID);
543 shortcutItem->highlightKeySequenceAsModified(keySequence != appliedKeySequence);
546 moduleItem->insertChild(newItemIdx, shortcutItem);
552 if (theUpdateSyncTrees) {
553 const std::set<QtxShortcutTree*>& syncTrees = QtxShortcutTree::instances[myShortcutContainer.get()];
554 for (const auto syncTree: syncTrees) {
555 if (syncTree == this)
558 syncTree->updateItems(theHighlightModified, false /*theUpdateSyncTrees*/);
559 const auto editDialog = syncTree->myEditDialog;
560 editDialog->setConfirmedKeySequence(myShortcutContainer->getKeySequence(editDialog->moduleID(), editDialog->inModuleActionID()));
561 editDialog->updateConflictsMessage();
566 /*! \returns Pointer and index of top-level item.
567 If the tree does not contain an item with theModuleID, returns {nullptr, -1}. */
568 std::pair<QtxShortcutTreeItem*, int> QtxShortcutTree::findModuleFolderItem(const QString& theModuleID) const
570 for (int moduleIdx = 0; moduleIdx < topLevelItemCount(); moduleIdx++) {
571 QtxShortcutTreeItem* moduleItem = static_cast<QtxShortcutTreeItem*>(topLevelItem(moduleIdx));
572 if (moduleItem->myModuleID == theModuleID)
573 return std::pair<QtxShortcutTreeItem*, int>(moduleItem, moduleIdx);
575 return std::pair<QtxShortcutTreeItem*, int>(nullptr, -1);
578 void QtxShortcutTree::onItemDoubleClicked(QTreeWidgetItem* theItem, int theColIdx)
580 QtxShortcutTreeItem* const item = static_cast<QtxShortcutTreeItem*>(theItem);
581 // Do not react if folder-item is clicked.
582 if (item->isFolder())
585 myEditDialog->setModuleAndActionID(item->myModuleID, item->myInModuleActionID);
586 myEditDialog->setModuleAndActionName(static_cast<QtxShortcutTreeItem*>(item->parent())->name(), item->name());
587 myEditDialog->setConfirmedKeySequence(QKeySequence::fromString(item->keySequence()));
588 myEditDialog->updateConflictsMessage();
589 const bool somethingChanged = myEditDialog->exec() == QDialog::Accepted;
591 if (!somethingChanged)
594 const QKeySequence newKeySequence = myEditDialog->editedKeySequence();
596 /** { moduleID, inModuleActionID }[] */
597 std::set<std::pair<QString, QString>> disabledActionIDs = myShortcutContainer->setShortcut(item->myModuleID, item->myInModuleActionID, newKeySequence, true /*override*/);
599 /** { moduleID, {inModuleActionID, keySequence}[] }[] */
600 std::map<QString, std::map<QString, QString>> changes;
601 changes[item->myModuleID][item->myInModuleActionID] = newKeySequence.toString();
602 for (const auto moduleAndActionID : disabledActionIDs) {
603 changes[moduleAndActionID.first][moduleAndActionID.second] = QString();
606 // Set new key sequences to shortcut items.
607 for (const auto& moduleIDAndChanges : changes) {
608 const QString& moduleID = moduleIDAndChanges.first;
610 const auto moduleItemAndIdx = findModuleFolderItem(moduleID);
611 const auto moduleItem = moduleItemAndIdx.first;
615 /** {inModuleActionID, newKeySequence}[] */
616 const std::map<QString, QString>& moduleChanges = moduleIDAndChanges.second;
618 // Go through module' shortcut items, and highlight those, whose key sequences differ from applied key sequences.
619 for (int childIdx = 0; childIdx < moduleItem->childCount(); childIdx++) {
620 QtxShortcutTreeItem* const childItem = static_cast<QtxShortcutTreeItem*>(moduleItem->child(childIdx));
621 const auto itChange = moduleChanges.find(childItem->myInModuleActionID);
622 if (itChange == moduleChanges.end()) {
623 // The shortcut has not been changed.
627 childItem->setKeySequence(itChange->second);
629 const QKeySequence& appliedKeySequence = SUIT_ShortcutMgr::get()->getKeySequence(moduleID, childItem->myInModuleActionID);
630 childItem->highlightKeySequenceAsModified(QKeySequence::fromString(itChange->second) != appliedKeySequence);
635 /*static*/ std::map<SUIT_ShortcutContainer*, std::set<QtxShortcutTree*>> QtxShortcutTree::instances =
636 std::map<SUIT_ShortcutContainer*, std::set<QtxShortcutTree*>>();
639 QtxShortcutTreeItem::QtxShortcutTreeItem(const QString& theModuleID, const QString& theInModuleActionID)
640 : QTreeWidgetItem(), myModuleID(theModuleID), myInModuleActionID(theInModuleActionID)
643 /*static*/ QtxShortcutTreeItem* QtxShortcutTreeItem::createFolderItem(const QString& theModuleID)
645 auto item = new QtxShortcutTreeItem(theModuleID, QString());
647 QFont font = item->font(QtxShortcutTree::ElementIdx::Name);
649 item->setFont(QtxShortcutTree::ElementIdx::Name, font);
654 /*! \returns nullptr if theInModuleActionID is empty. */
655 /*static*/ QtxShortcutTreeItem* QtxShortcutTreeItem::createShortcutItem(const QString& theModuleID, const QString& theInModuleActionID)
657 if (theInModuleActionID.isEmpty()) {
658 ShCutDbg("QtxShortcutTreeItem: attempt to create item with empty action ID.");
662 auto item = new QtxShortcutTreeItem(theModuleID, theInModuleActionID);
663 item->setToolTip(QtxShortcutTree::ElementIdx::KeySequence, QtxShortcutTree::tr("Double click to edit key sequence."));
667 bool QtxShortcutTreeItem::isFolder() const
669 return myInModuleActionID.isEmpty();
672 /*! \brief Highlights text at ElementIdx::KeySequence. */
673 void QtxShortcutTreeItem::highlightKeySequenceAsModified(bool theHighlight)
675 static const QBrush bgHighlitingBrush = QBrush(Qt::darkGreen);
676 static const QBrush fgHighlitingBrush = QBrush(Qt::white);
677 static const QBrush noBrush = QBrush();
679 setBackground(QtxShortcutTree::ElementIdx::KeySequence, theHighlight ? bgHighlitingBrush : noBrush);
680 setForeground(QtxShortcutTree::ElementIdx::KeySequence, theHighlight ? fgHighlitingBrush : noBrush);
683 void QtxShortcutTreeItem::setName(const QString& theName)
685 const QString& name = theName.isEmpty() ? myInModuleActionID : theName;
686 setText(QtxShortcutTree::ElementIdx::Name, name);
688 setToolTip(QtxShortcutTree::ElementIdx::Name, name + (name.at(name.length()-1) == "." ? "\n" : ".\n") + QtxShortcutTree::tr("Double click to edit key sequence."));
691 QString QtxShortcutTreeItem::name() const
693 return text(QtxShortcutTree::ElementIdx::Name);
696 void QtxShortcutTreeItem::setKeySequence(const QString& theKeySequence)
698 setText(QtxShortcutTree::ElementIdx::KeySequence, theKeySequence);
701 QString QtxShortcutTreeItem::keySequence() const
703 return text(QtxShortcutTree::ElementIdx::KeySequence);