File indexing completed on 2024-03-24 05:54:13
0001 /* 0002 SPDX-FileCopyrightText: 2008 Robert Knight <robertknight@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 // Own 0007 #include "KeyBindingEditor.h" 0008 0009 // Qt 0010 #include <QDialogButtonBox> 0011 #include <QIcon> 0012 #include <QKeyEvent> 0013 0014 // KDE 0015 #include <KLocalizedString> 0016 #include <KMessageBox> 0017 0018 // Konsole 0019 #include "keyboardtranslator/KeyboardTranslator.h" 0020 #include "keyboardtranslator/KeyboardTranslatorManager.h" 0021 #include "keyboardtranslator/KeyboardTranslatorReader.h" 0022 #include "ui_KeyBindingEditor.h" 0023 0024 #include "widgets/EditProfileDialog.h" 0025 0026 using namespace Konsole; 0027 0028 KeyBindingEditor::KeyBindingEditor(QWidget *parent) 0029 : QDialog(parent) 0030 , _ui(nullptr) 0031 , _translator(new KeyboardTranslator(QString())) 0032 , _isNewTranslator(false) 0033 { 0034 auto layout = new QVBoxLayout; 0035 0036 auto mainWidget = new QWidget(this); 0037 layout->addWidget(mainWidget); 0038 0039 auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 0040 buttonBox->button(QDialogButtonBox::Cancel)->setDefault(true); 0041 layout->addWidget(buttonBox); 0042 0043 setLayout(layout); 0044 0045 connect(buttonBox, &QDialogButtonBox::accepted, this, &Konsole::KeyBindingEditor::accept); 0046 connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); 0047 0048 setAttribute(Qt::WA_DeleteOnClose); 0049 0050 _ui = new Ui::KeyBindingEditor(); 0051 _ui->setupUi(mainWidget); 0052 0053 // description edit 0054 _ui->descriptionEdit->setPlaceholderText(i18nc("@label:textbox", "Enter descriptive label")); 0055 connect(_ui->descriptionEdit, &QLineEdit::textChanged, this, &Konsole::KeyBindingEditor::setTranslatorDescription); 0056 // filter edit 0057 connect(_ui->filterEdit, &QLineEdit::textChanged, this, &Konsole::KeyBindingEditor::filterRows); 0058 0059 // key bindings table 0060 _ui->keyBindingTable->setColumnCount(2); 0061 0062 QStringList labels; 0063 labels << i18n("Key Combination") << i18n("Output"); 0064 0065 _ui->keyBindingTable->setHorizontalHeaderLabels(labels); 0066 _ui->keyBindingTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); 0067 0068 _ui->keyBindingTable->verticalHeader()->hide(); 0069 _ui->keyBindingTable->setSelectionBehavior(QAbstractItemView::SelectRows); 0070 0071 // add and remove buttons 0072 _ui->addEntryButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); 0073 _ui->removeEntryButton->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); 0074 0075 connect(_ui->removeEntryButton, &QPushButton::clicked, this, &Konsole::KeyBindingEditor::removeSelectedEntry); 0076 connect(_ui->addEntryButton, &QPushButton::clicked, this, &Konsole::KeyBindingEditor::addNewEntry); 0077 0078 // test area 0079 _ui->testAreaInputEdit->installEventFilter(this); 0080 } 0081 0082 KeyBindingEditor::~KeyBindingEditor() 0083 { 0084 delete _ui; 0085 delete _translator; 0086 } 0087 0088 void KeyBindingEditor::filterRows(const QString &text) 0089 { 0090 const int rows = _ui->keyBindingTable->rowCount(); 0091 0092 QList<int> matchedRows; 0093 0094 for (QTableWidgetItem *matchedItem : _ui->keyBindingTable->findItems(text, Qt::MatchContains)) { 0095 matchedRows.append(matchedItem->row()); 0096 } 0097 0098 for (int i = 0; i < rows; i++) { 0099 if (matchedRows.contains(i) && _ui->keyBindingTable->isRowHidden(i)) { 0100 _ui->keyBindingTable->showRow(i); 0101 } else if (!matchedRows.contains(i)) { 0102 _ui->keyBindingTable->hideRow(i); 0103 } 0104 } 0105 } 0106 0107 void KeyBindingEditor::removeSelectedEntry() 0108 { 0109 QList<QTableWidgetItem *> uniqueList; 0110 const QList<QTableWidgetItem *> selectedItems = _ui->keyBindingTable->selectedItems(); 0111 for (QTableWidgetItem *item : selectedItems) { 0112 if (item->column() == 1) { // Select item at the first column 0113 item = _ui->keyBindingTable->item(item->row(), 0); 0114 } 0115 0116 if (!uniqueList.contains(item)) { 0117 uniqueList.append(item); 0118 } 0119 } 0120 0121 for (QTableWidgetItem *item : std::as_const(uniqueList)) { 0122 // get the first item in the row which has the entry 0123 0124 KeyboardTranslator::Entry existing = item->data(Qt::UserRole).value<KeyboardTranslator::Entry>(); 0125 0126 _translator->removeEntry(existing); 0127 0128 _ui->keyBindingTable->removeRow(item->row()); 0129 } 0130 } 0131 0132 void KeyBindingEditor::addNewEntry() 0133 { 0134 _ui->keyBindingTable->insertRow(_ui->keyBindingTable->rowCount()); 0135 0136 int newRowCount = _ui->keyBindingTable->rowCount(); 0137 0138 // block signals here to avoid triggering bindingTableItemChanged() slot call 0139 _ui->keyBindingTable->blockSignals(true); 0140 0141 _ui->keyBindingTable->setItem(newRowCount - 1, 0, new QTableWidgetItem()); 0142 _ui->keyBindingTable->setItem(newRowCount - 1, 1, new QTableWidgetItem()); 0143 0144 _ui->keyBindingTable->blockSignals(false); 0145 0146 // make sure user can see new row 0147 _ui->keyBindingTable->scrollToItem(_ui->keyBindingTable->item(newRowCount - 1, 0)); 0148 } 0149 0150 bool KeyBindingEditor::eventFilter(QObject *watched, QEvent *event) 0151 { 0152 if (watched == _ui->testAreaInputEdit) { 0153 if (event->type() == QEvent::KeyPress) { 0154 auto *keyEvent = static_cast<QKeyEvent *>(event); 0155 0156 // The state here is currently set to the state that a newly started 0157 // terminal in Konsole will be in ( which is also the same as the 0158 // state just after a reset ), this has 'ANSI' turned on and all other 0159 // states off. 0160 // 0161 // TODO: It may be useful to be able to specify the state in the 'test input' 0162 // area, but preferably not in a way which clutters the UI with lots of 0163 // checkboxes. 0164 // 0165 const KeyboardTranslator::States states = KeyboardTranslator::AnsiState; 0166 0167 KeyboardTranslator::Entry entry = _translator->findEntry(keyEvent->key(), keyEvent->modifiers(), states); 0168 0169 if (!entry.isNull()) { 0170 _ui->testAreaInputEdit->setText(entry.conditionToString()); 0171 _ui->testAreaOutputEdit->setText(entry.resultToString(true, keyEvent->modifiers())); 0172 } else { 0173 _ui->testAreaInputEdit->setText(keyEvent->text()); 0174 _ui->testAreaOutputEdit->setText(keyEvent->text()); 0175 } 0176 0177 keyEvent->accept(); 0178 return true; 0179 } 0180 } 0181 return QDialog::eventFilter(watched, event); 0182 } 0183 0184 void KeyBindingEditor::setDescription(const QString &description) 0185 { 0186 _ui->descriptionEdit->setText(description); 0187 0188 setTranslatorDescription(description); 0189 } 0190 0191 void KeyBindingEditor::setTranslatorDescription(const QString &description) 0192 { 0193 if (_translator != nullptr) { 0194 _translator->setDescription(description); 0195 } 0196 } 0197 0198 QString KeyBindingEditor::description() const 0199 { 0200 return _ui->descriptionEdit->text(); 0201 } 0202 0203 void KeyBindingEditor::setup(const KeyboardTranslator *translator, const QString ¤tProfileTranslator, bool isNewTranslator) 0204 { 0205 delete _translator; 0206 0207 _isNewTranslator = isNewTranslator; 0208 0209 _currentProfileTranslator = currentProfileTranslator; 0210 0211 _translator = new KeyboardTranslator(*translator); 0212 0213 // setup description edit line 0214 _ui->descriptionEdit->setClearButtonEnabled(true); 0215 // setup filter edit line 0216 _ui->filterEdit->setClearButtonEnabled(true); 0217 0218 if (_isNewTranslator) { 0219 setDescription(i18n("New Key Binding List")); 0220 setWindowTitle(i18n("New Key Binding List")); 0221 } else { 0222 _ui->descriptionEdit->setText(translator->description()); 0223 setWindowTitle(i18n("Edit Key Binding List")); 0224 } 0225 0226 // setup key binding table 0227 setupKeyBindingTable(translator); 0228 } 0229 0230 KeyboardTranslator *KeyBindingEditor::translator() const 0231 { 0232 return _translator; 0233 } 0234 0235 void KeyBindingEditor::bindingTableItemChanged(QTableWidgetItem *item) 0236 { 0237 QTableWidgetItem *key = _ui->keyBindingTable->item(item->row(), 0); 0238 KeyboardTranslator::Entry existing = key->data(Qt::UserRole).value<KeyboardTranslator::Entry>(); 0239 0240 QString condition = key->text(); 0241 QString result = _ui->keyBindingTable->item(item->row(), 1)->text(); 0242 0243 KeyboardTranslator::Entry entry = KeyboardTranslatorReader::createEntry(condition, result); 0244 _translator->replaceEntry(existing, entry); 0245 0246 // block signals to prevent this slot from being called repeatedly 0247 _ui->keyBindingTable->blockSignals(true); 0248 0249 key->setData(Qt::UserRole, QVariant::fromValue(entry)); 0250 0251 _ui->keyBindingTable->blockSignals(false); 0252 } 0253 0254 void KeyBindingEditor::setupKeyBindingTable(const KeyboardTranslator *translator) 0255 { 0256 disconnect(_ui->keyBindingTable, &QTableWidget::itemChanged, this, &Konsole::KeyBindingEditor::bindingTableItemChanged); 0257 0258 QList<KeyboardTranslator::Entry> entries = translator->entries(); 0259 _ui->keyBindingTable->setRowCount(entries.count()); 0260 0261 for (int row = 0; row < entries.count(); row++) { 0262 const KeyboardTranslator::Entry &entry = entries.at(row); 0263 0264 QTableWidgetItem *keyItem = new QTableWidgetItem(entry.conditionToString()); 0265 keyItem->setData(Qt::UserRole, QVariant::fromValue(entry)); 0266 0267 QTableWidgetItem *textItem = new QTableWidgetItem(entry.resultToString()); 0268 0269 _ui->keyBindingTable->setItem(row, 0, keyItem); 0270 _ui->keyBindingTable->setItem(row, 1, textItem); 0271 } 0272 _ui->keyBindingTable->sortItems(0); 0273 0274 connect(_ui->keyBindingTable, &QTableWidget::itemChanged, this, &Konsole::KeyBindingEditor::bindingTableItemChanged); 0275 } 0276 0277 void KeyBindingEditor::accept() 0278 { 0279 if (_translator == nullptr) { 0280 return; 0281 } 0282 0283 const auto newTranslator = new KeyboardTranslator(*_translator); 0284 0285 if (newTranslator->description().isEmpty()) { 0286 KMessageBox::error(this, i18n("A key bindings scheme cannot be saved with an empty description.")); 0287 delete newTranslator; 0288 return; 0289 } 0290 0291 if (_isNewTranslator) { 0292 newTranslator->setName(newTranslator->description()); 0293 } 0294 0295 KeyboardTranslatorManager::instance()->addTranslator(newTranslator); 0296 0297 const QString ¤tTranslatorName = newTranslator->name(); 0298 0299 Q_EMIT updateKeyBindingsListRequest(currentTranslatorName); 0300 0301 if (currentTranslatorName == _currentProfileTranslator) { 0302 Q_EMIT updateTempProfileKeyBindingsRequest(Profile::KeyBindings, currentTranslatorName); 0303 } 0304 0305 QDialog::accept(); 0306 } 0307 0308 QSize KeyBindingEditor::sizeHint() const 0309 { 0310 const auto parent = parentWidget(); 0311 if (parent != nullptr) { 0312 return {static_cast<int>(parent->width() * 0.9), static_cast<int>(parent->height() * 0.95)}; 0313 } 0314 0315 return {}; 0316 } 0317 0318 #include "moc_KeyBindingEditor.cpp"