/*
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;
}