2929#include < QKeySequence>
3030#include < QKeySequenceEdit>
3131#include < QMenu>
32+ #include < QMenuBar>
3233#include < QMessageBox>
3334#include < QPointer>
3435#include < QRadioButton>
@@ -422,7 +423,6 @@ void SettingsDialog::loadShortcutSettings() {
422423 QColor shortcutButtonActiveColor = darkMode ? Qt::white : palette.color (QPalette::ButtonText);
423424 QColor shortcutButtonInactiveColor = darkMode ? Qt::darkGray : palette.color (QPalette::Mid);
424425
425- const QList<QMenu *> menus = mainWindow->menuList ();
426426 ui->shortcutSearchLineEdit ->clear ();
427427 ui->shortcutTreeWidget ->clear ();
428428 ui->shortcutTreeWidget ->setColumnCount (3 );
@@ -438,6 +438,9 @@ void SettingsDialog::loadShortcutSettings() {
438438 QIcon::fromTheme (QStringLiteral (" edit-clear" ), QIcon (" :/icons/breeze-qownnotes/16x16/"
439439 " edit-clear.svg" ));
440440
441+ const QList<QMenu *> menus =
442+ mainWindow->menuBar ()->findChildren <QMenu *>(QString (), Qt::FindDirectChildrenOnly);
443+
441444 // loop through all top-level menus and build the tree recursively
442445 for (const QMenu *menu : menus) {
443446 if (disabledMenuNames.contains (menu->objectName ())) {
@@ -473,9 +476,37 @@ void SettingsDialog::buildShortcutTreeForMenu(const QMenu *menu, QTreeWidgetItem
473476 const QIcon &disableShortcutButtonIcon,
474477 const QIcon &clearButtonIcon,
475478 const QStringList &disabledMenuNames) {
476- auto *menuItem = new QTreeWidgetItem ();
479+ // Count eligible actions first to decide whether to add this menu at all
477480 int actionCount = 0 ;
478481
482+ foreach (QAction *action, menu->actions ()) {
483+ if (action->menu () != nullptr ) {
484+ if (!disabledMenuNames.contains (action->menu ()->objectName ())) {
485+ actionCount++;
486+ }
487+ } else if (!action->objectName ().isEmpty ()) {
488+ actionCount++;
489+ }
490+ }
491+
492+ if (actionCount == 0 ) {
493+ return ;
494+ }
495+
496+ // Add the menu item to the tree before populating children so that
497+ // setItemWidget() works correctly (items must be in the tree first)
498+ auto *menuItem = new QTreeWidgetItem ();
499+ menuItem->setText (0 , menu->title ().remove (QStringLiteral (" &" )));
500+ menuItem->setToolTip (0 , menu->objectName ());
501+
502+ if (parentItem == nullptr ) {
503+ ui->shortcutTreeWidget ->addTopLevelItem (menuItem);
504+ } else {
505+ parentItem->addChild (menuItem);
506+ }
507+
508+ menuItem->setExpanded (true );
509+
479510 // loop through all actions of the menu
480511 foreach (QAction *action, menu->actions ()) {
481512 // Handle submenus recursively so the full menu structure is visible
@@ -489,7 +520,6 @@ void SettingsDialog::buildShortcutTreeForMenu(const QMenu *menu, QTreeWidgetItem
489520 buildShortcutTreeForMenu (subMenu, menuItem, settings, shortcutButtonActiveColor,
490521 shortcutButtonInactiveColor, disableShortcutButtonIcon,
491522 clearButtonIcon, disabledMenuNames);
492- actionCount++;
493523 continue ;
494524 }
495525
@@ -561,23 +591,6 @@ void SettingsDialog::buildShortcutTreeForMenu(const QMenu *menu, QTreeWidgetItem
561591 .toString ());
562592
563593 ui->shortcutTreeWidget ->setItemWidget (actionItem, 2 , globalShortcutKeyWidget);
564-
565- actionCount++;
566- }
567-
568- if (actionCount > 0 ) {
569- menuItem->setText (0 , menu->title ().remove (QStringLiteral (" &" )));
570- menuItem->setToolTip (0 , menu->objectName ());
571-
572- if (parentItem == nullptr ) {
573- ui->shortcutTreeWidget ->addTopLevelItem (menuItem);
574- } else {
575- parentItem->addChild (menuItem);
576- }
577-
578- menuItem->setExpanded (true );
579- } else {
580- delete menuItem;
581594 }
582595}
583596
@@ -600,48 +613,53 @@ void SettingsDialog::keySequenceEvent(const QString &objectName) {
600613 return ;
601614 }
602615
603- // loop all top level tree widget items (menus)
604- for ( int i = 0 ; i < ui-> shortcutTreeWidget -> topLevelItemCount (); i++) {
605- QTreeWidgetItem *menuItem = ui-> shortcutTreeWidget -> topLevelItem (i );
616+ // Search all action items recursively for a conflicting shortcut
617+ std::function< bool (QTreeWidgetItem *)> checkItem = [&](QTreeWidgetItem *item) -> bool {
618+ const QString itemObjectName = item-> data ( 1 , Qt::UserRole). toString ( );
606619
607- // loop all tree widget items of the menu (action shortcuts)
608- for (int j = 0 ; j < menuItem->childCount (); j++) {
609- QTreeWidgetItem *shortcutItem = menuItem->child (j);
620+ if (!itemObjectName.isEmpty () && itemObjectName != objectName) {
621+ auto *frameWidget = ui->shortcutTreeWidget ->itemWidget (item, 1 );
610622
611- // skip the item that threw the event
612- if (shortcutItem->data (1 , Qt::UserRole).toString () == objectName) {
613- continue ;
614- }
623+ if (frameWidget != nullptr ) {
624+ const auto keySequenceWidgets = frameWidget->findChildren <QKeySequenceWidget *>();
615625
616- const auto keySequenceWidgets = ui->shortcutTreeWidget ->itemWidget (shortcutItem, 1 )
617- ->findChildren <QKeySequenceWidget *>();
626+ if (keySequenceWidgets.count () > 0 ) {
627+ QKeySequence keySequence = keySequenceWidgets.at (0 )->keySequence ();
628+
629+ // show an information if the shortcut was already used elsewhere
630+ if (keySequence == eventKeySequence) {
631+ if (Utils::Gui::information (
632+ this , tr (" Shortcut already assigned" ),
633+ tr (" The shortcut <strong>%1</strong> is already "
634+ " assigned to <strong>%2</strong>! Do you want to "
635+ " jump to the shortcut?" )
636+ .arg (eventKeySequence.toString (), item->text (0 )),
637+ QStringLiteral (" settings-shortcut-already-assigned" ),
638+ QMessageBox::Yes | QMessageBox::Cancel,
639+ QMessageBox::Yes) == QMessageBox::Yes) {
640+ ui->shortcutTreeWidget ->scrollToItem (item);
641+ ui->shortcutTreeWidget ->clearSelection ();
642+ item->setSelected (true );
643+ }
644+
645+ return true ;
646+ }
647+ }
648+ }
649+ }
618650
619- if (keySequenceWidgets.count () == 0 ) {
620- continue ;
651+ for (int i = 0 ; i < item->childCount (); i++) {
652+ if (checkItem (item->child (i))) {
653+ return true ;
621654 }
655+ }
622656
623- auto *keyWidget = keySequenceWidgets.at (0 );
624- QKeySequence keySequence = keyWidget->keySequence ();
625- QKeySequence defaultKeySequence = keyWidget->defaultKeySequence ();
626-
627- // show an information if the shortcut was already used elsewhere
628- if (keySequence == eventKeySequence) {
629- if (Utils::Gui::information (
630- this , tr (" Shortcut already assigned" ),
631- tr (" The shortcut <strong>%1</strong> is already "
632- " assigned to <strong>%2</strong>! Do you want to "
633- " jump to the shortcut?" )
634- .arg (eventKeySequence.toString (), shortcutItem->text (0 )),
635- QStringLiteral (" settings-shortcut-already-assigned" ),
636- QMessageBox::Yes | QMessageBox::Cancel,
637- QMessageBox::Yes) == QMessageBox::Yes) {
638- ui->shortcutTreeWidget ->scrollToItem (shortcutItem);
639- ui->shortcutTreeWidget ->clearSelection ();
640- shortcutItem->setSelected (true );
641- }
657+ return false ;
658+ };
642659
643- return ;
644- }
660+ for (int i = 0 ; i < ui->shortcutTreeWidget ->topLevelItemCount (); i++) {
661+ if (checkItem (ui->shortcutTreeWidget ->topLevelItem (i))) {
662+ return ;
645663 }
646664 }
647665}
@@ -651,92 +669,126 @@ void SettingsDialog::keySequenceEvent(const QString &objectName) {
651669 * of the assigned menu action
652670 */
653671QKeySequenceWidget *SettingsDialog::findKeySequenceWidget (const QString &objectName) {
654- // loop all top level tree widget items (menus)
655- for (int i = 0 ; i < ui->shortcutTreeWidget ->topLevelItemCount (); i++) {
656- QTreeWidgetItem *menuItem = ui->shortcutTreeWidget ->topLevelItem (i);
672+ QKeySequenceWidget *result = nullptr ;
673+
674+ std::function<void (QTreeWidgetItem *)> findItem = [&](QTreeWidgetItem *item) {
675+ if (result != nullptr ) {
676+ return ;
677+ }
657678
658- // loop all tree widget items of the menu (action shortcuts)
659- for (int j = 0 ; j < menuItem->childCount (); j++) {
660- QTreeWidgetItem *shortcutItem = menuItem->child (j);
661- QString name = shortcutItem->data (1 , Qt::UserRole).toString ();
679+ if (item->data (1 , Qt::UserRole).toString () == objectName) {
680+ auto *frameWidget = ui->shortcutTreeWidget ->itemWidget (item, 1 );
662681
663- if (name == objectName) {
664- const auto keySequenceWidgets = ui->shortcutTreeWidget ->itemWidget (shortcutItem, 1 )
665- ->findChildren <QKeySequenceWidget *>();
682+ if (frameWidget != nullptr ) {
683+ const auto keySequenceWidgets = frameWidget->findChildren <QKeySequenceWidget *>();
666684
667685 if (keySequenceWidgets.count () > 0 ) {
668- return keySequenceWidgets.at (0 );
686+ result = keySequenceWidgets.at (0 );
687+ return ;
669688 }
670689 }
671690 }
691+
692+ for (int i = 0 ; i < item->childCount (); i++) {
693+ findItem (item->child (i));
694+ }
695+ };
696+
697+ for (int i = 0 ; i < ui->shortcutTreeWidget ->topLevelItemCount (); i++) {
698+ findItem (ui->shortcutTreeWidget ->topLevelItem (i));
672699 }
673700
674- return nullptr ;
701+ return result ;
675702}
676703
677704/* *
678- * Stores the local and global keyboard shortcut settings
705+ * Finds the global QKeySequenceWidget in the shortcutTreeWidget by the
706+ * objectName of the assigned menu action (column 2)
679707 */
680- void SettingsDialog::storeShortcutSettings ( ) {
681- SettingsService settings ;
708+ QKeySequenceWidget * SettingsDialog::findGlobalKeySequenceWidget ( const QString &objectName ) {
709+ QKeySequenceWidget *result = nullptr ;
682710
683- // Collect all tree widget items recursively to find all action shortcut items,
684- // regardless of nesting depth (submenus can be multiple levels deep)
685- QList<QTreeWidgetItem *> allItems =
686- ui-> shortcutTreeWidget -> findItems ( QString (), Qt::MatchContains | Qt::MatchRecursive);
711+ std::function< void (QTreeWidgetItem *)> findItem = [&](QTreeWidgetItem *item) {
712+ if (result != nullptr ) {
713+ return ;
714+ }
687715
688- for (QTreeWidgetItem *shortcutItem : allItems) {
689- // Action items store their object name in column 1's UserRole;
690- // menu items do not, so skip those
691- const QString actionObjectName = shortcutItem->data (1 , Qt::UserRole).toString ();
716+ if (item->data (1 , Qt::UserRole).toString () == objectName) {
717+ result =
718+ dynamic_cast <QKeySequenceWidget *>(ui->shortcutTreeWidget ->itemWidget (item, 2 ));
692719
693- if (actionObjectName.isEmpty ()) {
694- continue ;
720+ if (result != nullptr ) {
721+ return ;
722+ }
695723 }
696724
697- auto *frameWidget = ui->shortcutTreeWidget ->itemWidget (shortcutItem, 1 );
698-
699- if (frameWidget == nullptr ) {
700- continue ;
725+ for (int i = 0 ; i < item->childCount (); i++) {
726+ findItem (item->child (i));
701727 }
728+ };
702729
703- const auto keySequenceWidgets = frameWidget->findChildren <QKeySequenceWidget *>();
730+ for (int i = 0 ; i < ui->shortcutTreeWidget ->topLevelItemCount (); i++) {
731+ findItem (ui->shortcutTreeWidget ->topLevelItem (i));
732+ }
704733
705- if (keySequenceWidgets.count () == 0 ) {
706- continue ;
707- }
734+ return result;
735+ }
708736
709- auto *keyWidget = keySequenceWidgets.at (0 );
710- auto *globalShortcutKeyWidget =
711- dynamic_cast <QKeySequenceWidget *>(ui->shortcutTreeWidget ->itemWidget (shortcutItem, 2 ));
737+ /* *
738+ * Stores the local and global keyboard shortcut settings
739+ */
740+ void SettingsDialog::storeShortcutSettings () {
741+ SettingsService settings;
742+ MainWindow *mainWindow = MainWindow::instance ();
712743
713- if (keyWidget == nullptr || globalShortcutKeyWidget == nullptr ) {
714- continue ;
715- }
744+ if (mainWindow == nullptr ) {
745+ return ;
746+ }
716747
717- // handle local shortcut
718- QKeySequence keySequence = keyWidget->keySequence ();
719- QKeySequence defaultKeySequence = keyWidget->defaultKeySequence ();
720- QString settingsKey = " Shortcuts/MainWindow-" + actionObjectName;
721-
722- // remove or store the setting for the shortcut if it's not default
723- if (keySequence == defaultKeySequence) {
724- settings.remove (settingsKey);
725- } else {
726- // set new key sequence (can also be empty if no key sequence
727- // should be used)
728- settings.setValue (settingsKey, keySequence);
729- }
748+ // Store shortcuts by iterating through the actual menu actions (the same
749+ // way initShortcuts reads them) and looking up the corresponding widgets
750+ // in the tree. This avoids any issues with tree widget item traversal.
751+ const QList<QMenu *> menus = mainWindow->menuList ();
752+
753+ for (QMenu *menu : menus) {
754+ for (QAction *action : menu->actions ()) {
755+ const QString actionObjectName = action->objectName ();
730756
731- // handle global shortcut
732- keySequence = globalShortcutKeyWidget->keySequence ();
733- settingsKey = " GlobalShortcuts/MainWindow-" + actionObjectName;
757+ if (actionObjectName.isEmpty ()) {
758+ continue ;
759+ }
760+
761+ // Find the key sequence widget for this action in the tree
762+ auto *keyWidget = findKeySequenceWidget (actionObjectName);
763+
764+ if (keyWidget != nullptr ) {
765+ QKeySequence keySequence = keyWidget->keySequence ();
766+ QKeySequence defaultKeySequence = keyWidget->defaultKeySequence ();
767+ const QString settingsKey =
768+ QStringLiteral (" Shortcuts/MainWindow-" ) + actionObjectName;
769+
770+ // remove or store the setting for the shortcut if it's not default
771+ if (keySequence == defaultKeySequence) {
772+ settings.remove (settingsKey);
773+ } else {
774+ settings.setValue (settingsKey, keySequence.toString ());
775+ }
776+ }
777+
778+ // Find the global shortcut widget for this action in the tree
779+ auto *globalWidget = findGlobalKeySequenceWidget (actionObjectName);
734780
735- // remove or store the setting for the shortcut if it's not empty
736- if (keySequence.isEmpty ()) {
737- settings.remove (settingsKey);
738- } else {
739- settings.setValue (settingsKey, keySequence);
781+ if (globalWidget != nullptr ) {
782+ QKeySequence keySequence = globalWidget->keySequence ();
783+ const QString settingsKey =
784+ QStringLiteral (" GlobalShortcuts/MainWindow-" ) + actionObjectName;
785+
786+ if (keySequence.isEmpty ()) {
787+ settings.remove (settingsKey);
788+ } else {
789+ settings.setValue (settingsKey, keySequence.toString ());
790+ }
791+ }
740792 }
741793 }
742794}
0 commit comments