15 #include <QHeaderView>
16 #include <QKeySequenceEdit>
18 #include <QMainWindow>
21 #include <QMessageBox>
23 #include <QTableWidget>
24 #include <QTableWidgetItem>
31 QAction* targetAction,
39 QString menuTitle = menu->title();
40 menuTitle.remove(
'&');
41 if (!menuTitle.isEmpty() && (
path.isEmpty() ||
path.last() != menuTitle)) {
42 path.append(menuTitle);
46 for (QAction* action : menu->actions()) {
47 if (action == targetAction) {
49 return path.join(
" > ");
53 QMenu* submenu = action->menu();
57 QStringList subPath =
path;
58 QString submenuTitle = submenu->title();
59 submenuTitle.remove(
'&');
61 if (!submenuTitle.isEmpty() &&
62 (subPath.isEmpty() || subPath.last() != submenuTitle)) {
63 subPath.append(submenuTitle);
77 static QString
getMenuPath(QAction* action, QWidget* parentWidget) {
84 QObject* parent = action->parent();
87 QMenu* menu = qobject_cast<QMenu*>(parent);
89 QString menuTitle = menu->title();
90 menuTitle.remove(
'&');
92 if (!menuTitle.isEmpty() &&
93 (
path.isEmpty() ||
path.first() != menuTitle)) {
94 path.prepend(menuTitle);
96 parent = menu->parent();
99 QAction* parentAction = qobject_cast<QAction*>(parent);
103 QMenu* submenu = parentAction->menu();
105 QString submenuTitle = submenu->title();
106 submenuTitle.remove(
'&');
107 if (!submenuTitle.isEmpty() &&
108 (
path.isEmpty() ||
path.first() != submenuTitle)) {
109 path.prepend(submenuTitle);
111 parent = submenu->parent();
113 QString actionTitle = parentAction->text();
114 actionTitle.remove(
'&');
115 if (!actionTitle.isEmpty() &&
116 (
path.isEmpty() ||
path.first() != actionTitle)) {
117 path.prepend(actionTitle);
119 parent = parentAction->parent();
127 if (!
path.isEmpty()) {
128 return path.join(
" > ");
133 QMenuBar* menuBar = parentWidget->findChild<QMenuBar*>();
136 QMainWindow* mainWindow = qobject_cast<QMainWindow*>(parentWidget);
138 menuBar = mainWindow->menuBar();
143 QStringList emptyPath;
144 for (QAction* menuAction : menuBar->actions()) {
145 QMenu* menu = menuAction->menu();
161 : QDialog(parent), m_ui(new Ui_ShortcutEditDialog) {
163 connect(m_ui->clearButton, &QPushButton::clicked, m_ui->keySequenceEdit,
164 &QKeySequenceEdit::clear);
170 return m_ui->keySequenceEdit->keySequence();
174 m_ui->keySequenceEdit->setKeySequence(sequence);
178 m_ui->keySequenceEdit->setFocus();
179 return QDialog::exec();
185 m_ui(new Ui_ShortcutDialog),
187 m_allActions(actions) {
189 m_ui->tableWidget->setRowCount(actions.count());
190 m_ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
192 connect(m_ui->tableWidget, &QTableWidget::itemDoubleClicked,
this,
193 &ecvShortcutDialog::handleDoubleClick);
194 connect(m_ui->searchLineEdit, &QLineEdit::textChanged,
this,
195 &ecvShortcutDialog::filterActions);
198 for (QAction* action : actions) {
200 QString displayText = action->text();
202 QString toolTip = action->toolTip();
205 QString cleanText = displayText;
206 cleanText.remove(
'&');
209 QString fullDescription = cleanText;
212 if (!menuPath.isEmpty()) {
213 fullDescription +=
" (" + menuPath +
")";
217 if (!toolTip.isEmpty() && toolTip != displayText &&
218 toolTip != cleanText) {
220 QString cleanToolTip = toolTip;
221 cleanToolTip.remove(
'&');
222 if (cleanToolTip != cleanText && !cleanToolTip.isEmpty()) {
223 fullDescription +=
" - " + cleanToolTip;
228 new QTableWidgetItem(action->icon(), fullDescription);
229 actionWidget->setFlags(actionWidget->flags() & ~Qt::ItemIsEditable);
232 actionWidget->setData(Qt::UserRole + 1, cleanText);
233 actionWidget->setData(Qt::UserRole + 2, menuPath);
234 actionWidget->setData(Qt::UserRole + 3, toolTip);
238 auto* shortcutWidget =
239 new QTableWidgetItem(action->shortcut().toString());
240 shortcutWidget->setFlags(actionWidget->flags() & ~Qt::ItemIsEditable);
241 shortcutWidget->setData(Qt::UserRole, QVariant::fromValue(action));
251 m_ui->tableWidget->horizontalHeader()->setSectionResizeMode(
256 int shortcutContentWidth =
260 const int minShortcutWidth = 200;
261 int shortcutWidth = qMax(shortcutContentWidth + 30, minShortcutWidth);
265 m_ui->tableWidget->horizontalHeader()->setSectionResizeMode(
269 m_ui->tableWidget->horizontalHeader()->setStretchLastSection(
false);
270 m_ui->tableWidget->horizontalHeader()->setSectionResizeMode(
283 for (
int i = 0; i < m_ui->tableWidget->rowCount(); i++) {
284 QTableWidgetItem* item =
286 auto* action = item->data(Qt::UserRole).value<QAction*>();
288 if (settings.contains(action->text())) {
289 const QKeySequence defaultValue;
290 const auto sequence = settings.value(action->text(), defaultValue)
291 .value<QKeySequence>();
293 item->setText(sequence.toString());
294 action->setShortcut(sequence);
300 const QAction* ecvShortcutDialog::checkConflict(
301 const QKeySequence& sequence)
const {
302 for (
int i = 0; i < m_ui->tableWidget->rowCount(); i++) {
303 const QTableWidgetItem* item = m_ui->tableWidget->item(i, 1);
304 const auto* action = item->data(Qt::UserRole).value<QAction*>();
305 if (action->shortcut() == sequence) {
313 void ecvShortcutDialog::handleDoubleClick(QTableWidgetItem* item) {
322 auto* action = item->data(Qt::UserRole).value<QAction*>();
325 if (m_editDialog->
exec() == QDialog::Rejected) {
329 const QKeySequence keySequence = m_editDialog->
keySequence();
330 if (keySequence == action->shortcut()) {
335 if (!keySequence.isEmpty()) {
336 const QAction* conflict = checkConflict(keySequence);
338 QMessageBox::critical(
339 this, tr(
"Shortcut conflict"),
340 QString(tr(
"The shortcut entered would conflict with the "
342 .arg(conflict->text()));
347 item->setText(keySequence.toString());
348 action->setShortcut(keySequence);
352 settings.setValue(action->text(), keySequence);
355 void ecvShortcutDialog::filterActions(
const QString& searchText) {
356 if (searchText.isEmpty()) {
361 QString searchLower = searchText.toLower();
362 for (
int row = 0; row < m_ui->tableWidget->rowCount(); ++row) {
363 QTableWidgetItem* item =
367 bool matches = item->text().toLower().contains(searchLower);
371 QString originalText =
372 item->data(Qt::UserRole + 1).toString().toLower();
374 item->data(Qt::UserRole + 2).toString().toLower();
376 item->data(Qt::UserRole + 3).toString().toLower();
378 matches = originalText.contains(searchLower) ||
379 menuPath.contains(searchLower) ||
380 toolTip.contains(searchLower);
383 m_ui->tableWidget->setRowHidden(row, !matches);
388 void ecvShortcutDialog::showAllRows() {
389 for (
int row = 0; row < m_ui->tableWidget->rowCount(); ++row) {
390 m_ui->tableWidget->setRowHidden(row,
false);
static const QString Shortcuts()
void restoreShortcutsFromQSettings() const
~ecvShortcutDialog() override
ecvShortcutDialog(const QList< QAction * > &actions, QWidget *parent=nullptr)
~ecvShortcutEditDialog() override
void setKeySequence(const QKeySequence &sequence) const
ecvShortcutEditDialog(QWidget *parent=nullptr)
QKeySequence keySequence() const
constexpr int ACTION_NAME_COLUMN
static QString getMenuPath(QAction *action, QWidget *parentWidget)
static QString findMenuPathRecursive(QMenu *menu, QAction *targetAction, QStringList path)
constexpr int KEY_SEQUENCE_COLUMN
static const std::string path