diff --git a/cmake/qet_compilation_vars.cmake b/cmake/qet_compilation_vars.cmake index 0b47ffd64..f01467e33 100644 --- a/cmake/qet_compilation_vars.cmake +++ b/cmake/qet_compilation_vars.cmake @@ -248,6 +248,8 @@ set(QET_SRC_FILES ${QET_DIR}/sources/qtextorientationwidget.h ${QET_DIR}/sources/recentfiles.cpp ${QET_DIR}/sources/recentfiles.h + ${QET_DIR}/sources/shortcutmanager.cpp + ${QET_DIR}/sources/shortcutmanager.h ${QET_DIR}/sources/titleblockcell.cpp ${QET_DIR}/sources/titleblockcell.h ${QET_DIR}/sources/titleblockproperties.cpp @@ -732,6 +734,8 @@ set(QET_SRC_FILES ${QET_DIR}/sources/ui/configpage/projectconfigpages.h ${QET_DIR}/sources/ui/configpage/guidespropertieswidget.cpp ${QET_DIR}/sources/ui/configpage/guidespropertieswidget.h + ${QET_DIR}/sources/ui/configpage/shortcutsconfigpage.cpp + ${QET_DIR}/sources/ui/configpage/shortcutsconfigpage.h ${QET_DIR}/sources/undocommand/addelementtextcommand.cpp ${QET_DIR}/sources/undocommand/addelementtextcommand.h diff --git a/sources/autoNum/ui/autonumberingdockwidget.cpp b/sources/autoNum/ui/autonumberingdockwidget.cpp index 4d99f94af..97f2d2f1b 100644 --- a/sources/autoNum/ui/autonumberingdockwidget.cpp +++ b/sources/autoNum/ui/autonumberingdockwidget.cpp @@ -20,6 +20,7 @@ #include "../../diagram.h" #include "../../diagramview.h" #include "../../qetapp.h" +#include "../../shortcutmanager.h" #include "../../titleblockproperties.h" #include "../../ui/projectpropertiesdialog.h" #include "../numerotationcontext.h" @@ -151,7 +152,8 @@ void AutoNumberingDockWidget::setProject(QETProject *project, //Set Combobox Context setContext(); - ui->m_configure_pb->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_P); + ShortcutManager::instance().registerAction(ui->m_configure_pb, "autonum.configure", + tr("Autonumérotation"), Qt::CTRL | Qt::SHIFT | Qt::Key_P); } /** diff --git a/sources/editor/ui/qetelementeditor.cpp b/sources/editor/ui/qetelementeditor.cpp index 90828fa7e..ba8d80f19 100644 --- a/sources/editor/ui/qetelementeditor.cpp +++ b/sources/editor/ui/qetelementeditor.cpp @@ -19,6 +19,7 @@ #include "ui_qetelementeditor.h" #include "../elementscene.h" #include "../../qeticons.h" +#include "../../shortcutmanager.h" #include "../elementview.h" #include "../../qetmessagebox.h" #include "../../qetapp.h" @@ -978,32 +979,32 @@ void QETElementEditor::setupActions() m_redo_action = m_elmt_scene -> undoStack().createRedoAction(this, tr("Refaire")); m_undo_action -> setIcon(QET::Icons::EditUndo); m_redo_action -> setIcon(QET::Icons::EditRedo); - m_undo_action -> setShortcuts(QKeySequence::Undo); - m_redo_action -> setShortcuts(QKeySequence::Redo); + ShortcutManager::instance().registerAction(m_undo_action, "elementeditor.undo", tr("Éditeur d'élément"), QKeySequence::Undo); + ShortcutManager::instance().registerAction(m_redo_action, "elementeditor.redo", tr("Éditeur d'élément"), QKeySequence::Redo); ui->m_undo_toolbar->addAction(m_undo_action); ui->m_undo_toolbar->addAction(m_redo_action); - ui->m_new_action -> setShortcut(QKeySequence::New); - ui->m_open_action -> setShortcut(QKeySequence::Open); - ui->m_open_from_file_action -> setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_O); - ui->m_save_action -> setShortcut(QKeySequence::Save); - ui->m_save_as_file_action -> setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_S); - ui->m_select_all_act -> setShortcut(QKeySequence::SelectAll); - ui->m_deselect_all_action -> setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_A); - ui->m_revert_selection_action -> setShortcut(Qt::CTRL | Qt::Key_I); - ui->m_cut_action -> setShortcut(QKeySequence::Cut); - ui->m_copy_action -> setShortcut(QKeySequence::Copy); - ui->m_paste_action -> setShortcut(QKeySequence::Paste); - ui->m_paste_in_area_action -> setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_V); - ui->m_edit_names_action -> setShortcut(Qt::CTRL | Qt::Key_E); - ui->m_edit_author_action -> setShortcut(Qt::CTRL | Qt::Key_Y); + ShortcutManager::instance().registerAction(ui->m_new_action, "elementeditor.new", tr("Éditeur d'élément"), QKeySequence::New); + ShortcutManager::instance().registerAction(ui->m_open_action, "elementeditor.open", tr("Éditeur d'élément"), QKeySequence::Open); + ShortcutManager::instance().registerAction(ui->m_open_from_file_action, "elementeditor.open_from_file", tr("Éditeur d'élément"), Qt::CTRL | Qt::SHIFT | Qt::Key_O); + ShortcutManager::instance().registerAction(ui->m_save_action, "elementeditor.save", tr("Éditeur d'élément"), QKeySequence::Save); + ShortcutManager::instance().registerAction(ui->m_save_as_file_action, "elementeditor.save_as_file", tr("Éditeur d'élément"), Qt::CTRL | Qt::SHIFT | Qt::Key_S); + ShortcutManager::instance().registerAction(ui->m_select_all_act, "elementeditor.select_all", tr("Éditeur d'élément"), QKeySequence::SelectAll); + ShortcutManager::instance().registerAction(ui->m_deselect_all_action, "elementeditor.deselect_all", tr("Éditeur d'élément"), Qt::CTRL | Qt::SHIFT | Qt::Key_A); + ShortcutManager::instance().registerAction(ui->m_revert_selection_action, "elementeditor.revert_selection", tr("Éditeur d'élément"), Qt::CTRL | Qt::Key_I); + ShortcutManager::instance().registerAction(ui->m_cut_action, "elementeditor.cut", tr("Éditeur d'élément"), QKeySequence::Cut); + ShortcutManager::instance().registerAction(ui->m_copy_action, "elementeditor.copy", tr("Éditeur d'élément"), QKeySequence::Copy); + ShortcutManager::instance().registerAction(ui->m_paste_action, "elementeditor.paste", tr("Éditeur d'élément"), QKeySequence::Paste); + ShortcutManager::instance().registerAction(ui->m_paste_in_area_action, "elementeditor.paste_in_area", tr("Éditeur d'élément"), Qt::CTRL | Qt::SHIFT | Qt::Key_V); + ShortcutManager::instance().registerAction(ui->m_edit_names_action, "elementeditor.edit_names", tr("Éditeur d'élément"), Qt::CTRL | Qt::Key_E); + ShortcutManager::instance().registerAction(ui->m_edit_author_action, "elementeditor.edit_author", tr("Éditeur d'élément"), Qt::CTRL | Qt::Key_Y); #ifdef Q_OS_MAC - ui->m_delete_action -> setShortcut(Qt::Key_Backspace); - ui->m_quit_action -> setShortcut(Qt::CTRL | Qt::Key_W); + ShortcutManager::instance().registerAction(ui->m_delete_action, "elementeditor.delete", tr("Éditeur d'élément"), Qt::Key_Backspace); + ShortcutManager::instance().registerAction(ui->m_quit_action, "elementeditor.quit", tr("Éditeur d'élément"), Qt::CTRL | Qt::Key_W); #else - ui->m_delete_action -> setShortcut(Qt::Key_Delete); - ui->m_quit_action -> setShortcut(Qt::CTRL | Qt::Key_Q); + ShortcutManager::instance().registerAction(ui->m_delete_action, "elementeditor.delete", tr("Éditeur d'élément"), Qt::Key_Delete); + ShortcutManager::instance().registerAction(ui->m_quit_action, "elementeditor.quit", tr("Éditeur d'élément"), Qt::CTRL | Qt::Key_Q); #endif //Depth action @@ -1019,27 +1020,27 @@ void QETElementEditor::setupActions() addToolBar(Qt::TopToolBarArea, depth_toolbar); //Rotate action - ui->m_rotate_action -> setShortcut(Qt::Key_Space); + ShortcutManager::instance().registerAction(ui->m_rotate_action, "elementeditor.rotate", tr("Éditeur d'élément"), Qt::Key_Space); connect(ui->m_rotate_action, &QAction::triggered, [this]() {this -> elementScene() -> undoStack().push(new RotateElementsCommand(this->elementScene()));}); //Rotate Fine action = rotate with smaller inkrement - ui->m_rotateFine_action -> setShortcut(Qt::CTRL | Qt::Key_Space); + ShortcutManager::instance().registerAction(ui->m_rotateFine_action, "elementeditor.rotate_fine", tr("Éditeur d'élément"), Qt::CTRL | Qt::Key_Space); connect(ui->m_rotateFine_action, &QAction::triggered, [this]() {this -> elementScene() -> undoStack().push(new RotateFineElementsCommand(this->elementScene()));}); //Flip action - ui->m_flip_action -> setShortcut(Qt::Key_F); + ShortcutManager::instance().registerAction(ui->m_flip_action, "elementeditor.flip", tr("Éditeur d'élément"), Qt::Key_F); connect(ui->m_flip_action, &QAction::triggered, [this]() {this -> elementScene() -> undoStack().push(new FlipElementsCommand(this->elementScene()));}); //Mirror action - ui->m_mirror_action -> setShortcut(Qt::Key_M); + ShortcutManager::instance().registerAction(ui->m_mirror_action, "elementeditor.mirror", tr("Éditeur d'élément"), Qt::Key_M); connect(ui->m_mirror_action, &QAction::triggered, [this]() {this -> elementScene() -> undoStack().push(new MirrorElementsCommand(this->elementScene()));}); //Zoom action - ui->m_zoom_in_action -> setShortcut(QKeySequence::ZoomIn); - ui->m_zoom_out_action -> setShortcut(QKeySequence::ZoomOut); - ui->m_zoom_fit_best_action -> setShortcut(Qt::CTRL | Qt::Key_9); - ui->m_zoom_original_action -> setShortcut(Qt::CTRL | Qt::Key_0); + ShortcutManager::instance().registerAction(ui->m_zoom_in_action, "elementeditor.zoom_in", tr("Éditeur d'élément"), QKeySequence::ZoomIn); + ShortcutManager::instance().registerAction(ui->m_zoom_out_action, "elementeditor.zoom_out", tr("Éditeur d'élément"), QKeySequence::ZoomOut); + ShortcutManager::instance().registerAction(ui->m_zoom_fit_best_action, "elementeditor.zoom_fit_best", tr("Éditeur d'élément"), Qt::CTRL | Qt::Key_9); + ShortcutManager::instance().registerAction(ui->m_zoom_original_action, "elementeditor.zoom_original", tr("Éditeur d'élément"), Qt::CTRL | Qt::Key_0); //Add primitive actions m_add_part_action_grp = new QActionGroup(this); diff --git a/sources/elementspanelwidget.cpp b/sources/elementspanelwidget.cpp index fb77cdf48..7baf0afbe 100644 --- a/sources/elementspanelwidget.cpp +++ b/sources/elementspanelwidget.cpp @@ -22,6 +22,7 @@ #include "qetapp.h" #include "qeticons.h" #include "qetproject.h" +#include "shortcutmanager.h" #include "titleblock/templatedeleter.h" #include #include @@ -74,14 +75,14 @@ ElementsPanelWidget::ElementsPanelWidget(QWidget *parent) : QWidget(parent) { tbt_remove = new QAction(QET::Icons::TitleBlock, tr("Supprimer ce modèle"), this); - prj_del_diagram -> setShortcut(QKeySequence(Qt::Key_Delete)); - prj_move_diagram_up -> setShortcut(QKeySequence(Qt::Key_F3)); - prj_move_diagram_down -> setShortcut(QKeySequence(Qt::Key_F4)); - prj_move_diagram_top -> setShortcut(QKeySequence(Qt::Key_F5)); - prj_move_diagram_downx10 -> setShortcut(QKeySequence(Qt::Key_F6)); - prj_move_diagram_downx100 -> setShortcut(QKeySequence(Qt::Key_F7)); - prj_move_diagram_upx10 -> setShortcut(QKeySequence(Qt::Key_F8)); - prj_move_diagram_upx100 -> setShortcut(QKeySequence(Qt::Key_F9)); + ShortcutManager::instance().registerAction(prj_del_diagram, "panel.delete_diagram", tr("Panneau des éléments"), QKeySequence(Qt::Key_Delete)); + ShortcutManager::instance().registerAction(prj_move_diagram_up, "panel.move_diagram_up", tr("Panneau des éléments"), QKeySequence(Qt::Key_F3)); + ShortcutManager::instance().registerAction(prj_move_diagram_down, "panel.move_diagram_down", tr("Panneau des éléments"), QKeySequence(Qt::Key_F4)); + ShortcutManager::instance().registerAction(prj_move_diagram_top, "panel.move_diagram_top", tr("Panneau des éléments"), QKeySequence(Qt::Key_F5)); + ShortcutManager::instance().registerAction(prj_move_diagram_downx10, "panel.move_diagram_downx10", tr("Panneau des éléments"), QKeySequence(Qt::Key_F6)); + ShortcutManager::instance().registerAction(prj_move_diagram_downx100, "panel.move_diagram_downx100", tr("Panneau des éléments"), QKeySequence(Qt::Key_F7)); + ShortcutManager::instance().registerAction(prj_move_diagram_upx10, "panel.move_diagram_upx10", tr("Panneau des éléments"), QKeySequence(Qt::Key_F8)); + ShortcutManager::instance().registerAction(prj_move_diagram_upx100, "panel.move_diagram_upx100", tr("Panneau des éléments"), QKeySequence(Qt::Key_F9)); diff --git a/sources/qet.cpp b/sources/qet.cpp index 03a1e138d..7d57f2999 100644 --- a/sources/qet.cpp +++ b/sources/qet.cpp @@ -17,6 +17,7 @@ */ #include "qet.h" #include "qeticons.h" +#include "shortcutmanager.h" #include #include @@ -785,10 +786,10 @@ QActionGroup *QET::depthActionGroup(QObject *parent) edit_lower ->setStatusTip(QObject::tr("Éloigne la ou les sélections")); edit_backward->setStatusTip(QObject::tr("Envoie en arrière plan la ou les sélections")); - edit_raise ->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Up); - edit_lower ->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Down); - edit_backward->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_End); - edit_forward ->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Home); + ShortcutManager::instance().registerAction(edit_raise, "depth.raise", QObject::tr("Profondeur"), Qt::CTRL | Qt::SHIFT | Qt::Key_Up); + ShortcutManager::instance().registerAction(edit_lower, "depth.lower", QObject::tr("Profondeur"), Qt::CTRL | Qt::SHIFT | Qt::Key_Down); + ShortcutManager::instance().registerAction(edit_backward, "depth.backward", QObject::tr("Profondeur"), Qt::CTRL | Qt::SHIFT | Qt::Key_End); + ShortcutManager::instance().registerAction(edit_forward, "depth.forward", QObject::tr("Profondeur"), Qt::CTRL | Qt::SHIFT | Qt::Key_Home); edit_forward ->setData(QET::BringForward); edit_raise ->setData(QET::Raise); diff --git a/sources/qetapp.cpp b/sources/qetapp.cpp index deb20b161..8545f99e7 100644 --- a/sources/qetapp.cpp +++ b/sources/qetapp.cpp @@ -36,6 +36,7 @@ #include "titleblocktemplate.h" #include "ui/aboutqetdialog.h" #include "ui/configpage/generalconfigurationpage.h" +#include "ui/configpage/shortcutsconfigpage.h" #include "machine_info.h" #include "TerminalStrip/ui/terminalstripeditorwindow.h" #include "qetversion.h" @@ -2051,6 +2052,7 @@ void QETApp::configureQET() cd.addPage(new NewDiagramPage()); cd.addPage(new ExportConfigPage()); cd.addPage(new PrintConfigPage()); + cd.addPage(new ShortcutsConfigPage()); // associates the dialog with a possible parent widget // associe le dialogue a un eventuel widget parent diff --git a/sources/qetdiagrameditor.cpp b/sources/qetdiagrameditor.cpp index 0ce820416..bb80e09ee 100644 --- a/sources/qetdiagrameditor.cpp +++ b/sources/qetdiagrameditor.cpp @@ -38,6 +38,7 @@ #include "qeticons.h" #include "qetmessagebox.h" #include "recentfiles.h" +#include "shortcutmanager.h" #include "ui/bomexportdialog.h" #include "ui/diagrampropertieseditordockwidget.h" #include "ui/backupdialog.h" @@ -275,7 +276,7 @@ void QETDiagramEditor::setUpActions() { //Export to another file type (jpeg, dxf etc...) m_export_to_images = new QAction(QET::Icons::DocumentExport, tr("E&xporter"), this); - m_export_to_images->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_X); + ShortcutManager::instance().registerAction(m_export_to_images, "diagrameditor.export_to_images", tr("Éditeur de schémas"), Qt::CTRL | Qt::SHIFT | Qt::Key_X); m_export_to_images->setStatusTip(tr("Exporte le folio courant dans un autre format", "status bar tip")); connect(m_export_to_images, &QAction::triggered, [this]() { ProjectView *current_project = currentProjectView(); @@ -286,7 +287,7 @@ void QETDiagramEditor::setUpActions() //Print m_print = new QAction(QET::Icons::DocumentPrint, tr("Imprimer"), this); - m_print->setShortcut(QKeySequence::Print); + ShortcutManager::instance().registerAction(m_print, "diagrameditor.print", tr("Éditeur de schémas"), QKeySequence::Print); m_print->setStatusTip(tr("Imprime un ou plusieurs folios du projet courant", "status bar tip")); connect(m_print, &QAction::triggered, [this]() { auto project = currentProject(); @@ -307,19 +308,19 @@ void QETDiagramEditor::setUpActions() //Quit editor m_quit_editor = new QAction(QET::Icons::ApplicationExit, tr("&Quitter"), this); - m_quit_editor->setShortcut(Qt::CTRL | Qt::Key_Q); + ShortcutManager::instance().registerAction(m_quit_editor, "diagrameditor.quit", tr("Éditeur de schémas"), Qt::CTRL | Qt::Key_Q); m_quit_editor->setStatusTip(tr("Ferme l'application QElectroTech", "status bar tip")); connect(m_quit_editor, &QAction::triggered, this, &QETDiagramEditor::close); //Undo undo = undo_group.createUndoAction(this, tr("Annuler")); undo->setIcon(QET::Icons::EditUndo); - undo->setShortcut(QKeySequence::Undo); + ShortcutManager::instance().registerAction(undo, "diagrameditor.undo", tr("Éditeur de schémas"), QKeySequence::Undo); undo->setStatusTip(tr("Annule l'action précédente", "status bar tip")); //Redo redo = undo_group.createRedoAction(this, tr("Refaire")); redo->setIcon(QET::Icons::EditRedo); - redo->setShortcut(QKeySequence::Redo); + ShortcutManager::instance().registerAction(redo, "diagrameditor.redo", tr("Éditeur de schémas"), QKeySequence::Redo); redo->setStatusTip(tr("Restaure l'action annulée", "status bar tip")); //cut copy past @@ -327,9 +328,9 @@ void QETDiagramEditor::setUpActions() m_copy = new QAction(QET::Icons::EditCopy, tr("Cop&ier"), this); m_paste = new QAction(QET::Icons::EditPaste, tr("C&oller"), this); - m_cut -> setShortcut(QKeySequence::Cut); - m_copy -> setShortcut(QKeySequence::Copy); - m_paste -> setShortcut(QKeySequence::Paste); + ShortcutManager::instance().registerAction(m_cut, "diagrameditor.cut", tr("Éditeur de schémas"), QKeySequence::Cut); + ShortcutManager::instance().registerAction(m_copy, "diagrameditor.copy", tr("Éditeur de schémas"), QKeySequence::Copy); + ShortcutManager::instance().registerAction(m_paste, "diagrameditor.paste", tr("Éditeur de schémas"), QKeySequence::Paste); m_cut -> setStatusTip(tr("Transfère les éléments sélectionnés dans le presse-papier", "status bar tip")); m_copy -> setStatusTip(tr("Copie les éléments sélectionnés dans le presse-papier", "status bar tip")); @@ -350,7 +351,7 @@ void QETDiagramEditor::setUpActions() //Reset conductor path m_conductor_reset = new QAction(QET::Icons::ConductorSettings, tr("Réinitialiser les conducteurs"), this); - m_conductor_reset->setShortcut(Qt::CTRL | Qt::Key_K); + ShortcutManager::instance().registerAction(m_conductor_reset, "diagrameditor.conductor_reset", tr("Éditeur de schémas"), Qt::CTRL | Qt::Key_K); m_conductor_reset->setStatusTip(tr("Recalcule les chemins des conducteurs sans tenir compte des modifications", "status bar tip")); connect(m_conductor_reset, &QAction::triggered, [this]() { if (DiagramView *dv = currentDiagramView()) @@ -404,7 +405,7 @@ void QETDiagramEditor::setUpActions() //Edit current diagram properties m_edit_diagram_properties = new QAction(QET::Icons::DialogInformation, tr("Propriétés du folio"), this); - m_edit_diagram_properties->setShortcut(Qt::CTRL | Qt::Key_L); + ShortcutManager::instance().registerAction(m_edit_diagram_properties, "diagrameditor.edit_diagram_properties", tr("Éditeur de schémas"), Qt::CTRL | Qt::Key_L); m_edit_diagram_properties -> setStatusTip(tr("Édite les propriétés du folio (dimensions, informations du cartouche, propriétés des conducteurs...)", "status bar tip")); connect(m_edit_diagram_properties, &QAction::triggered, [this]() { if (ProjectView *project_view = currentProjectView()) @@ -422,7 +423,7 @@ void QETDiagramEditor::setUpActions() //Add new folio to current project m_project_add_diagram = new QAction(QET::Icons::DiagramAdd, tr("Ajouter un folio"), this); - m_project_add_diagram->setShortcut(Qt::CTRL | Qt::Key_T); + ShortcutManager::instance().registerAction(m_project_add_diagram, "diagrameditor.project_add_diagram", tr("Éditeur de schémas"), Qt::CTRL | Qt::Key_T); connect(m_project_add_diagram, &QAction::triggered, [this]() { if (ProjectView *current_project = currentProjectView()) { current_project->project()->addNewDiagram(); @@ -566,12 +567,12 @@ void QETDiagramEditor::setUpActions() //Navigate next/previous project m_next_window = new QAction(tr("Projet suivant"), this); - m_next_window->setShortcut(QKeySequence::NextChild); + ShortcutManager::instance().registerAction(m_next_window, "diagrameditor.next_window", tr("Éditeur de schémas"), QKeySequence::NextChild); m_next_window->setStatusTip(tr("Active le projet suivant", "status bar tip")); connect(m_next_window, &QAction::triggered, &m_workspace, &QMdiArea::activateNextSubWindow); m_previous_window = new QAction(tr("Projet précédent"), this); - m_previous_window->setShortcut(QKeySequence::PreviousChild); + ShortcutManager::instance().registerAction(m_previous_window, "diagrameditor.previous_window", tr("Éditeur de schémas"), QKeySequence::PreviousChild); m_previous_window->setStatusTip(tr("Active le projet précédent", "status bar tip")); connect(m_previous_window, &QAction::triggered, &m_workspace, &QMdiArea::activatePreviousSubWindow); @@ -582,11 +583,11 @@ void QETDiagramEditor::setUpActions() m_save_file_as = m_file_actions_group.addAction(QET::Icons::DocumentSaveAs, tr("Enregistrer sous")); m_close_file = m_file_actions_group.addAction(QET::Icons::ProjectClose, tr("&Fermer")); - new_file ->setShortcut(QKeySequence::New); - open_file ->setShortcut(QKeySequence::Open); - m_close_file ->setShortcut(QKeySequence::Close); - m_save_file ->setShortcut(QKeySequence::Save); - m_save_file_as ->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_S); + ShortcutManager::instance().registerAction(new_file, "diagrameditor.new_file", tr("Éditeur de schémas"), QKeySequence::New); + ShortcutManager::instance().registerAction(open_file, "diagrameditor.open_file", tr("Éditeur de schémas"), QKeySequence::Open); + ShortcutManager::instance().registerAction(m_close_file, "diagrameditor.close_file", tr("Éditeur de schémas"), QKeySequence::Close); + ShortcutManager::instance().registerAction(m_save_file, "diagrameditor.save_file", tr("Éditeur de schémas"), QKeySequence::Save); + ShortcutManager::instance().registerAction(m_save_file_as, "diagrameditor.save_file_as", tr("Éditeur de schémas"), Qt::CTRL | Qt::SHIFT | Qt::Key_S); new_file ->setStatusTip( tr("Crée un nouveau projet", "status bar tip") ); open_file ->setStatusTip( tr("Ouvre un projet existant", "status bar tip") ); @@ -630,10 +631,10 @@ void QETDiagramEditor::setUpActions() m_edit_selection = m_selection_actions_group.addAction( QET::Icons::ElementEdit, tr("Éditer l'item sélectionné") ); m_group_selected_texts = m_selection_actions_group.addAction( QET::Icons::textGroup, tr("Grouper les textes sélectionnés")); - m_delete_selection->setShortcut(Qt::Key_Delete); - m_rotate_selection->setShortcut(Qt::Key_Space); - m_rotate_texts ->setShortcut(Qt::CTRL | Qt::Key_Space); - m_edit_selection ->setShortcut(Qt::CTRL | Qt::Key_E); + ShortcutManager::instance().registerAction(m_delete_selection, "diagrameditor.delete_selection", tr("Éditeur de schémas"), Qt::Key_Delete); + ShortcutManager::instance().registerAction(m_rotate_selection, "diagrameditor.rotate_selection", tr("Éditeur de schémas"), Qt::Key_Space); + ShortcutManager::instance().registerAction(m_rotate_texts, "diagrameditor.rotate_texts", tr("Éditeur de schémas"), Qt::CTRL | Qt::Key_Space); + ShortcutManager::instance().registerAction(m_edit_selection, "diagrameditor.edit_selection", tr("Éditeur de schémas"), Qt::CTRL | Qt::Key_E); m_delete_selection->setStatusTip( tr("Enlève les éléments sélectionnés du folio", "status bar tip")); m_rotate_selection->setStatusTip( tr("Pivote les éléments et textes sélectionnés", "status bar tip")); @@ -654,9 +655,9 @@ void QETDiagramEditor::setUpActions() QAction *select_nothing = m_select_actions_group.addAction( QET::Icons::EditSelectNone, tr("Désélectionner tout") ); QAction *select_invert = m_select_actions_group.addAction( QET::Icons::EditSelectInvert, tr("Inverser la sélection") ); - select_all ->setShortcut(QKeySequence::SelectAll); - select_nothing->setShortcut(QKeySequence::Deselect); - select_invert ->setShortcut(Qt::CTRL | Qt::Key_I); + ShortcutManager::instance().registerAction(select_all, "diagrameditor.select_all", tr("Éditeur de schémas"), QKeySequence::SelectAll); + ShortcutManager::instance().registerAction(select_nothing, "diagrameditor.select_nothing", tr("Éditeur de schémas"), QKeySequence::Deselect); + ShortcutManager::instance().registerAction(select_invert, "diagrameditor.select_invert", tr("Éditeur de schémas"), Qt::CTRL | Qt::Key_I); select_all ->setStatusTip( tr("Sélectionne tous les éléments du folio", "status bar tip") ); select_nothing->setStatusTip( tr("Désélectionne tous les éléments du folio", "status bar tip") ); @@ -676,11 +677,11 @@ void QETDiagramEditor::setUpActions() QAction *zoom_reset = m_zoom_actions_group.addAction( QET::Icons::ZoomOriginal, tr("Pas de zoom")); m_zoom_action_toolBar << zoom_content << zoom_fit << zoom_reset; - zoom_in ->setShortcut(QKeySequence::ZoomIn); - zoom_out ->setShortcut(QKeySequence::ZoomOut); - zoom_content->setShortcut(Qt::CTRL | Qt::Key_8); - zoom_fit ->setShortcut(Qt::CTRL | Qt::Key_9); - zoom_reset ->setShortcut(Qt::CTRL | Qt::Key_0); + ShortcutManager::instance().registerAction(zoom_in, "diagrameditor.zoom_in", tr("Éditeur de schémas"), QKeySequence::ZoomIn); + ShortcutManager::instance().registerAction(zoom_out, "diagrameditor.zoom_out", tr("Éditeur de schémas"), QKeySequence::ZoomOut); + ShortcutManager::instance().registerAction(zoom_content, "diagrameditor.zoom_content", tr("Éditeur de schémas"), Qt::CTRL | Qt::Key_8); + ShortcutManager::instance().registerAction(zoom_fit, "diagrameditor.zoom_fit", tr("Éditeur de schémas"), Qt::CTRL | Qt::Key_9); + ShortcutManager::instance().registerAction(zoom_reset, "diagrameditor.zoom_reset", tr("Éditeur de schémas"), Qt::CTRL | Qt::Key_0); zoom_in ->setStatusTip(tr("Agrandit le folio", "status bar tip")); zoom_out ->setStatusTip(tr("Rétrécit le folio", "status bar tip")); @@ -738,7 +739,7 @@ void QETDiagramEditor::setUpActions() }); m_find = new QAction(tr("Chercher/remplacer"), this); - m_find->setShortcut(QKeySequence::Find); + ShortcutManager::instance().registerAction(m_find, "diagrameditor.find", tr("Éditeur de schémas"), QKeySequence::Find); connect(m_find, &QAction::triggered, [this]() { if (auto animator = m_search_and_replace_widget.findChild("search and replace animator")) { diff --git a/sources/qetmainwindow.cpp b/sources/qetmainwindow.cpp index d203325fd..6061e99d8 100644 --- a/sources/qetmainwindow.cpp +++ b/sources/qetmainwindow.cpp @@ -24,6 +24,7 @@ #include "qetmainwindow.h" #include "qeticons.h" +#include "shortcutmanager.h" #include "qetapp.h" #include "qetdiagrameditor.h" #include "projectview.h" @@ -98,7 +99,7 @@ void QETMainWindow::initCommonActions() QDesktopServices::openUrl(QUrl(link)); }); - manual_online_ -> setShortcut(Qt::Key_F1); + ShortcutManager::instance().registerAction(manual_online_, "mainwindow.manual_online", tr("Général"), Qt::Key_F1); youtube_ = new QAction(QET::Icons::QETVideo, tr("Chaine Youtube"), this); youtube_ -> setStatusTip(tr("Lance le navigateur par défaut vers la chaine Youtube de QElectroTech", "status bar tip")); @@ -220,7 +221,7 @@ void QETMainWindow::updateFullScreenAction() fullscreen_action_ -> setIcon(QET::Icons::FullScreenEnter); fullscreen_action_ -> setStatusTip(tr("Affiche QElectroTech en mode plein écran", "status bar tip")); } - fullscreen_action_ -> setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_F); + ShortcutManager::instance().registerAction(fullscreen_action_, "mainwindow.fullscreen", tr("Général"), Qt::CTRL | Qt::SHIFT | Qt::Key_F); } /** diff --git a/sources/richtext/richtexteditor.cpp b/sources/richtext/richtexteditor.cpp index cfc9e270d..20afb4cc0 100644 --- a/sources/richtext/richtexteditor.cpp +++ b/sources/richtext/richtexteditor.cpp @@ -47,6 +47,7 @@ #include "richtexteditor_p.h" #include "ui_addlinkdialog.h" +#include "../shortcutmanager.h" //#include @@ -461,19 +462,19 @@ RichTextEditorToolBar::RichTextEditorToolBar(RichTextEditor *editor, m_bold_action = createCheckableAction( QIcon(":/ico/32x32/format-text-bold.png"), tr("Texte en gras"), editor, SLOT(setFontBold(bool)), this); - m_bold_action->setShortcut(Qt::CTRL | Qt::Key_B); + ShortcutManager::instance().registerAction(m_bold_action, "richtext.bold", tr("Éditeur de texte"), Qt::CTRL | Qt::Key_B); addAction(m_bold_action); m_italic_action = createCheckableAction( QIcon(":/ico/32x32/format-text-italic.png"), tr("Texte en italique"), editor, SLOT(setFontItalic(bool)), this); - m_italic_action->setShortcut(Qt::CTRL | Qt::Key_I); + ShortcutManager::instance().registerAction(m_italic_action, "richtext.italic", tr("Éditeur de texte"), Qt::CTRL | Qt::Key_I); addAction(m_italic_action); m_underline_action = createCheckableAction( QIcon(":/ico/32x32/format-text-underline.png"), tr("Texte souligé"), editor, SLOT(setFontUnderline(bool)), this); - m_underline_action->setShortcut(Qt::CTRL | Qt::Key_U); + ShortcutManager::instance().registerAction(m_underline_action, "richtext.underline", tr("Éditeur de texte"), Qt::CTRL | Qt::Key_U); addAction(m_underline_action); diff --git a/sources/shortcutmanager.cpp b/sources/shortcutmanager.cpp new file mode 100644 index 000000000..083c4a44a --- /dev/null +++ b/sources/shortcutmanager.cpp @@ -0,0 +1,167 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#include "shortcutmanager.h" + +#include +#include +#include +#include + +namespace { + const QString SETTINGS_GROUP = QStringLiteral("shortcuts/"); +} + +ShortcutManager &ShortcutManager::instance() +{ + static ShortcutManager manager; + return manager; +} + +/** + @brief ShortcutManager::savedSequence + @param id + @param default_sequence + @return the user-overridden sequence stored for \a id, or \a default_sequence + if the user never overrode it. A stored-but-empty sequence (the user + cleared the shortcut) is returned as an empty QKeySequence, not the default. +*/ +QKeySequence ShortcutManager::savedSequence(const QString &id, const QKeySequence &default_sequence) const +{ + QSettings settings; + const QString key = SETTINGS_GROUP + id; + if (!settings.contains(key)) { + return default_sequence; + } + return QKeySequence::fromString(settings.value(key).toString()); +} + +/** + @brief ShortcutManager::registerAction + Register \a target under \a id, applying the user's saved override (if + any) or \a default_sequence otherwise. \a target must be a QAction or a + QAbstractButton -- both declare a "shortcut" QKeySequence property, which + is what's actually read/written here, so this works for either without a + separate code path. Safe to call once per instance that shares this id + (e.g. once per open window of the same kind) -- the first call to reach a + given id fixes its category, description (taken from the target's current + "text" property) and default sequence for the lifetime of the + application; later calls just register another live target to update. +*/ +void ShortcutManager::registerAction(QObject *target, const QString &id, + const QString &category, + const QKeySequence &default_sequence) +{ + if (!target) { + return; + } + + auto it = m_entries.find(id); + if (it == m_entries.end()) { + Entry entry; + entry.category = category; + entry.description = target->property("text").toString(); + entry.description.remove(QLatin1Char('&')); + entry.default_sequence = default_sequence; + it = m_entries.insert(id, entry); + m_order << id; + } + + QList> &targets = it->targets; + targets.erase(std::remove_if(targets.begin(), targets.end(), + [](const QPointer &t) { return t.isNull(); }), + targets.end()); + if (!targets.contains(target)) { + targets << target; + } + + target->setProperty("shortcut", QVariant::fromValue(savedSequence(id, it->default_sequence))); +} + +/** + @return every registered shortcut, in registration order, along with its + currently effective sequence -- for display in the Shortcuts config page. +*/ +QList ShortcutManager::allShortcuts() const +{ + QList list; + for (const QString &id : m_order) { + const Entry &entry = m_entries.value(id); + ShortcutInfo info; + info.id = id; + info.category = entry.category; + info.description = entry.description; + info.default_sequence = entry.default_sequence; + info.current_sequence = savedSequence(id, entry.default_sequence); + list << info; + } + return list; +} + +QKeySequence ShortcutManager::currentSequence(const QString &id) const +{ + auto it = m_entries.find(id); + if (it == m_entries.end()) { + return QKeySequence(); + } + return savedSequence(id, it->default_sequence); +} + +/** + @brief ShortcutManager::setSequence + Persist \a sequence as the binding for \a id and apply it immediately to + every currently live QAction registered under that id. Persisted as "no + override" when \a sequence matches the id's default, so a future QET + version raising that default takes effect for users who never customized it. +*/ +void ShortcutManager::setSequence(const QString &id, const QKeySequence &sequence) +{ + auto it = m_entries.find(id); + if (it == m_entries.end()) { + return; + } + + QSettings settings; + const QString key = SETTINGS_GROUP + id; + if (sequence == it->default_sequence) { + settings.remove(key); + } else { + settings.setValue(key, sequence.toString()); + } + + for (const QPointer &target : qAsConst(it->targets)) { + if (target) { + target->setProperty("shortcut", QVariant::fromValue(sequence)); + } + } +} + +void ShortcutManager::resetToDefault(const QString &id) +{ + auto it = m_entries.find(id); + if (it == m_entries.end()) { + return; + } + setSequence(id, it->default_sequence); +} + +void ShortcutManager::resetAllToDefaults() +{ + for (const QString &id : m_order) { + resetToDefault(id); + } +} diff --git a/sources/shortcutmanager.h b/sources/shortcutmanager.h new file mode 100644 index 000000000..4306895c8 --- /dev/null +++ b/sources/shortcutmanager.h @@ -0,0 +1,92 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#ifndef SHORTCUTMANAGER_H +#define SHORTCUTMANAGER_H + +#include +#include +#include +#include +#include +#include + +class QObject; + +/** + @brief The ShortcutManager class + App-wide registry of every rebindable shortcut, carried by either a + QAction or a QAbstractButton -- both declare an identical "shortcut" + Q_PROPERTY of type QKeySequence, which this class targets generically via + QObject so it doesn't need a separate code path for the handful of + QAbstractButton-based shortcuts (e.g. a QPushButton acting as a shortcut- + only trigger). + + Every place in the code that used to call setShortcut() directly now + calls registerAction() instead, giving each shortcut a stable string id, a + category (for grouping in the UI) and a hardcoded default sequence. + registerAction() applies the user's saved override for that id (if any), + falling back to the default, and remembers the target so the Shortcuts + configuration page can list, edit and persist it. + + Several targets can share the same id at once, since QET allows several + windows of the same kind (diagram editor, element editor...) to be open + simultaneously, each with its own QAction. setSequence() updates every + live target for a given id in one call. +*/ +class ShortcutManager +{ + public: + static ShortcutManager &instance(); + + struct ShortcutInfo { + QString id; + QString category; + QString description; + QKeySequence default_sequence; + QKeySequence current_sequence; + }; + + void registerAction(QObject *target, const QString &id, + const QString &category, + const QKeySequence &default_sequence); + + QList allShortcuts() const; + QKeySequence currentSequence(const QString &id) const; + void setSequence(const QString &id, const QKeySequence &sequence); + void resetToDefault(const QString &id); + void resetAllToDefaults(); + + private: + ShortcutManager() = default; + ShortcutManager(const ShortcutManager &) = delete; + void operator=(const ShortcutManager &) = delete; + + struct Entry { + QString category; + QString description; + QKeySequence default_sequence; + QList> targets; + }; + + QKeySequence savedSequence(const QString &id, const QKeySequence &default_sequence) const; + + QHash m_entries; + QStringList m_order; +}; + +#endif // SHORTCUTMANAGER_H diff --git a/sources/titleblock/qettemplateeditor.cpp b/sources/titleblock/qettemplateeditor.cpp index 685549ca8..9f41c88d2 100644 --- a/sources/titleblock/qettemplateeditor.cpp +++ b/sources/titleblock/qettemplateeditor.cpp @@ -19,6 +19,7 @@ #include "../qetapp.h" #include "../qeticons.h" +#include "../shortcutmanager.h" #include "../qetmessagebox.h" #include "../qetproject.h" #include "templatecellwidget.h" @@ -390,25 +391,25 @@ void QETTitleBlockTemplateEditor::initActions() undo_ -> setIcon(QET::Icons::EditUndo); redo_ -> setIcon(QET::Icons::EditRedo); - new_ -> setShortcut(QKeySequence::New); - open_ -> setShortcut(QKeySequence::Open); - open_from_file_ -> setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_O); - save_ -> setShortcut(QKeySequence::Save); - save_as_file_ -> setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_S); - quit_ -> setShortcut(Qt::CTRL | Qt::Key_Q); - undo_ -> setShortcut(QKeySequence::Undo); - redo_ -> setShortcut(QKeySequence::Redo); - cut_ -> setShortcut(QKeySequence::Cut); - copy_ -> setShortcut(QKeySequence::Copy); - paste_ -> setShortcut(QKeySequence::Paste); - edit_logos_ -> setShortcut(Qt::CTRL | Qt::Key_T); - edit_info_ -> setShortcut(Qt::CTRL | Qt::Key_Y); - merge_cells_ -> setShortcut(Qt::CTRL | Qt::Key_J); - split_cell_ -> setShortcut(Qt::CTRL | Qt::Key_K); - zoom_in_ -> setShortcut(QKeySequence::ZoomIn); - zoom_out_ -> setShortcut(QKeySequence::ZoomOut); - zoom_fit_ -> setShortcut(Qt::CTRL | Qt::Key_9); - zoom_reset_ -> setShortcut(Qt::CTRL | Qt::Key_0); + ShortcutManager::instance().registerAction(new_, "titleblockeditor.new", tr("Éditeur de cartouche"), QKeySequence::New); + ShortcutManager::instance().registerAction(open_, "titleblockeditor.open", tr("Éditeur de cartouche"), QKeySequence::Open); + ShortcutManager::instance().registerAction(open_from_file_, "titleblockeditor.open_from_file", tr("Éditeur de cartouche"), Qt::CTRL | Qt::SHIFT | Qt::Key_O); + ShortcutManager::instance().registerAction(save_, "titleblockeditor.save", tr("Éditeur de cartouche"), QKeySequence::Save); + ShortcutManager::instance().registerAction(save_as_file_, "titleblockeditor.save_as_file", tr("Éditeur de cartouche"), Qt::CTRL | Qt::SHIFT | Qt::Key_S); + ShortcutManager::instance().registerAction(quit_, "titleblockeditor.quit", tr("Éditeur de cartouche"), Qt::CTRL | Qt::Key_Q); + ShortcutManager::instance().registerAction(undo_, "titleblockeditor.undo", tr("Éditeur de cartouche"), QKeySequence::Undo); + ShortcutManager::instance().registerAction(redo_, "titleblockeditor.redo", tr("Éditeur de cartouche"), QKeySequence::Redo); + ShortcutManager::instance().registerAction(cut_, "titleblockeditor.cut", tr("Éditeur de cartouche"), QKeySequence::Cut); + ShortcutManager::instance().registerAction(copy_, "titleblockeditor.copy", tr("Éditeur de cartouche"), QKeySequence::Copy); + ShortcutManager::instance().registerAction(paste_, "titleblockeditor.paste", tr("Éditeur de cartouche"), QKeySequence::Paste); + ShortcutManager::instance().registerAction(edit_logos_, "titleblockeditor.edit_logos", tr("Éditeur de cartouche"), Qt::CTRL | Qt::Key_T); + ShortcutManager::instance().registerAction(edit_info_, "titleblockeditor.edit_info", tr("Éditeur de cartouche"), Qt::CTRL | Qt::Key_Y); + ShortcutManager::instance().registerAction(merge_cells_, "titleblockeditor.merge_cells", tr("Éditeur de cartouche"), Qt::CTRL | Qt::Key_J); + ShortcutManager::instance().registerAction(split_cell_, "titleblockeditor.split_cell", tr("Éditeur de cartouche"), Qt::CTRL | Qt::Key_K); + ShortcutManager::instance().registerAction(zoom_in_, "titleblockeditor.zoom_in", tr("Éditeur de cartouche"), QKeySequence::ZoomIn); + ShortcutManager::instance().registerAction(zoom_out_, "titleblockeditor.zoom_out", tr("Éditeur de cartouche"), QKeySequence::ZoomOut); + ShortcutManager::instance().registerAction(zoom_fit_, "titleblockeditor.zoom_fit", tr("Éditeur de cartouche"), Qt::CTRL | Qt::Key_9); + ShortcutManager::instance().registerAction(zoom_reset_, "titleblockeditor.zoom_reset", tr("Éditeur de cartouche"), Qt::CTRL | Qt::Key_0); connect(new_, SIGNAL(triggered()), this, SLOT(newTemplate())); connect(open_, SIGNAL(triggered()), this, SLOT(open())); diff --git a/sources/ui/configpage/shortcutsconfigpage.cpp b/sources/ui/configpage/shortcutsconfigpage.cpp new file mode 100644 index 000000000..f5bf58f94 --- /dev/null +++ b/sources/ui/configpage/shortcutsconfigpage.cpp @@ -0,0 +1,227 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#include "shortcutsconfigpage.h" + +#include "../../qeticons.h" +#include "../../shortcutmanager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + @brief ShortcutsConfigPage::ShortcutsConfigPage + @param parent +*/ +ShortcutsConfigPage::ShortcutsConfigPage(QWidget *parent) : + ConfigPage(parent) +{ + auto *vlayout = new QVBoxLayout(); + + QLabel *title_label = new QLabel(this->title()); + vlayout->addWidget(title_label); + + QFrame *horiz_line = new QFrame(); + horiz_line->setFrameShape(QFrame::HLine); + vlayout->addWidget(horiz_line); + + m_filter_edit = new QLineEdit(this); + m_filter_edit->setPlaceholderText(tr("Filtrer les raccourcis…")); + connect(m_filter_edit, &QLineEdit::textChanged, this, &ShortcutsConfigPage::filterRows); + vlayout->addWidget(m_filter_edit); + + m_table = new QTableWidget(0, 4, this); + m_table->setHorizontalHeaderLabels({tr("Catégorie"), tr("Action"), tr("Raccourci"), QString()}); + m_table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + m_table->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); + m_table->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents); + m_table->horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeToContents); + m_table->verticalHeader()->setVisible(false); + m_table->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_table->setSelectionMode(QAbstractItemView::NoSelection); + vlayout->addWidget(m_table); + + auto *reset_all_button = new QPushButton(tr("Tout réinitialiser"), this); + connect(reset_all_button, &QPushButton::clicked, this, &ShortcutsConfigPage::resetAllRows); + + auto *bottom_layout = new QHBoxLayout(); + bottom_layout->addStretch(); + bottom_layout->addWidget(reset_all_button); + vlayout->addLayout(bottom_layout); + + setLayout(vlayout); + + populateTable(); +} + +ShortcutsConfigPage::~ShortcutsConfigPage() +{ +} + +/** + @brief ShortcutsConfigPage::populateTable + Fill the table with one row per shortcut known to ShortcutManager, sorted + by category then action name. +*/ +void ShortcutsConfigPage::populateTable() +{ + QList shortcuts = ShortcutManager::instance().allShortcuts(); + std::sort(shortcuts.begin(), shortcuts.end(), + [](const ShortcutManager::ShortcutInfo &a, const ShortcutManager::ShortcutInfo &b) { + if (a.category != b.category) { + return a.category < b.category; + } + return a.description < b.description; + }); + + m_table->setRowCount(shortcuts.size()); + m_rows.clear(); + m_rows.reserve(shortcuts.size()); + + for (int row = 0; row < shortcuts.size(); ++row) { + const ShortcutManager::ShortcutInfo &info = shortcuts.at(row); + + auto *category_item = new QTableWidgetItem(info.category); + category_item->setFlags(category_item->flags() & ~Qt::ItemIsEditable); + m_table->setItem(row, 0, category_item); + + auto *description_item = new QTableWidgetItem(info.description); + description_item->setFlags(description_item->flags() & ~Qt::ItemIsEditable); + m_table->setItem(row, 1, description_item); + + auto *edit = new QKeySequenceEdit(info.current_sequence, m_table); + connect(edit, &QKeySequenceEdit::editingFinished, this, &ShortcutsConfigPage::checkConflicts); + m_table->setCellWidget(row, 2, edit); + + auto *reset_button = new QToolButton(m_table); + reset_button->setIcon(QET::Icons::EditUndo); + reset_button->setToolTip(tr("Réinitialiser ce raccourci")); + reset_button->setAutoRaise(true); + connect(reset_button, &QToolButton::clicked, this, [this, row]() { resetRow(row); }); + m_table->setCellWidget(row, 3, reset_button); + + m_rows << Row{info.id, info.default_sequence, edit}; + } + + checkConflicts(); +} + +/** + @brief ShortcutsConfigPage::filterRows + Hide every row whose category or action name doesn't contain \a filter_text. +*/ +void ShortcutsConfigPage::filterRows(const QString &filter_text) +{ + const QString needle = filter_text.trimmed(); + for (int row = 0; row < m_table->rowCount(); ++row) { + const bool matches = needle.isEmpty() + || m_table->item(row, 0)->text().contains(needle, Qt::CaseInsensitive) + || m_table->item(row, 1)->text().contains(needle, Qt::CaseInsensitive); + m_table->setRowHidden(row, !matches); + } +} + +/** + @brief ShortcutsConfigPage::checkConflicts + Highlight every row whose currently-edited sequence is shared, non-empty, + with another row, and explain the conflict in the shortcut editor's tooltip. +*/ +void ShortcutsConfigPage::checkConflicts() +{ + QHash> sequence_to_rows; + for (int row = 0; row < m_rows.size(); ++row) { + const QString sequence_text = m_rows.at(row).edit->keySequence().toString(); + if (!sequence_text.isEmpty()) { + sequence_to_rows[sequence_text] << row; + } + } + + for (int row = 0; row < m_rows.size(); ++row) { + const Row ¤t_row = m_rows.at(row); + const QString sequence_text = current_row.edit->keySequence().toString(); + const QList &conflicting_rows = sequence_to_rows.value(sequence_text); + const bool conflicted = !sequence_text.isEmpty() && conflicting_rows.size() > 1; + + QTableWidgetItem *description_item = m_table->item(row, 1); + if (conflicted) { + QStringList other_descriptions; + for (int other_row : conflicting_rows) { + if (other_row != row) { + other_descriptions << m_table->item(other_row, 1)->text(); + } + } + description_item->setBackground(QColor(255, 205, 205)); + current_row.edit->setToolTip( + tr("Ce raccourci est aussi utilisé par : %1").arg(other_descriptions.join(QStringLiteral(", ")))); + } else { + description_item->setBackground(Qt::NoBrush); + current_row.edit->setToolTip(QString()); + } + } +} + +/** + @brief ShortcutsConfigPage::resetRow + Reset the shortcut editor at \a row_index to its default sequence. +*/ +void ShortcutsConfigPage::resetRow(int row_index) +{ + if (row_index < 0 || row_index >= m_rows.size()) { + return; + } + m_rows.at(row_index).edit->setKeySequence(m_rows.at(row_index).default_sequence); + checkConflicts(); +} + +void ShortcutsConfigPage::resetAllRows() +{ + for (const Row &row : qAsConst(m_rows)) { + row.edit->setKeySequence(row.default_sequence); + } + checkConflicts(); +} + +/** + @brief ShortcutsConfigPage::applyConf + Persist every row's shortcut edit through ShortcutManager, which also + applies it immediately to every currently live QAction sharing that id. +*/ +void ShortcutsConfigPage::applyConf() +{ + for (const Row &row : qAsConst(m_rows)) { + ShortcutManager::instance().setSequence(row.id, row.edit->keySequence()); + } +} + +QString ShortcutsConfigPage::title() const +{ + return tr("Raccourcis", "configuration page title"); +} + +QIcon ShortcutsConfigPage::icon() const +{ + return QET::Icons::ConfigureToolbars; +} diff --git a/sources/ui/configpage/shortcutsconfigpage.h b/sources/ui/configpage/shortcutsconfigpage.h new file mode 100644 index 000000000..ba00e9806 --- /dev/null +++ b/sources/ui/configpage/shortcutsconfigpage.h @@ -0,0 +1,68 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#ifndef SHORTCUTSCONFIGPAGE_H +#define SHORTCUTSCONFIGPAGE_H + +#include "configpage.h" + +#include + +class QKeySequenceEdit; +class QLineEdit; +class QTableWidget; + +/** + @brief The ShortcutsConfigPage class + Configuration page listing every shortcut registered with ShortcutManager, + letting the user rebind, reset and search them. Bindings are only + persisted (via ShortcutManager::setSequence()) when applyConf() runs, i.e. + when the user validates the surrounding ConfigDialog. +*/ +class ShortcutsConfigPage : public ConfigPage +{ + Q_OBJECT + + public: + explicit ShortcutsConfigPage(QWidget *parent = nullptr); + ~ShortcutsConfigPage() override; + + void applyConf() override; + QString title() const override; + QIcon icon() const override; + + private slots: + void filterRows(const QString &filter_text); + void checkConflicts(); + void resetAllRows(); + + private: + struct Row { + QString id; + QKeySequence default_sequence; + QKeySequenceEdit *edit; + }; + + void populateTable(); + void resetRow(int row_index); + + QLineEdit *m_filter_edit; + QTableWidget *m_table; + QList m_rows; +}; + +#endif // SHORTCUTSCONFIGPAGE_H