File indexing completed on 2024-05-05 03:58:34

0001 /*
0002     SPDX-FileCopyrightText: KDE Developers
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include <QFileDialog>
0008 #include <QTableWidgetItem>
0009 #include <QWhatsThis>
0010 
0011 #include <KLocalizedString>
0012 #include <KMessageBox>
0013 
0014 #include "ui_configwidget.h"
0015 #include <utils/kateconfig.h>
0016 #include <utils/kateglobal.h>
0017 #include <vimode/config/configtab.h>
0018 #include <vimode/keyparser.h>
0019 
0020 using namespace KateVi;
0021 
0022 ConfigTab::ConfigTab(QWidget *parent, Mappings *mappings)
0023     : KateConfigPage(parent)
0024     , m_mappings(mappings)
0025 {
0026     // This will let us have more separation between this page and
0027     // the QTabWidget edge (ereslibre)
0028     QVBoxLayout *layout = new QVBoxLayout(this);
0029     QWidget *newWidget = new QWidget(this);
0030 
0031     ui = new Ui::ConfigWidget();
0032     ui->setupUi(newWidget);
0033 
0034     // Make the header take all the width in equal parts.
0035     ui->tblNormalModeMappings->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
0036     ui->tblInsertModeMappings->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
0037     ui->tblVisualModeMappings->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
0038 
0039     // What's This? help can be found in the ui file
0040     reload();
0041 
0042     //
0043     // after initial reload, connect the stuff for the changed() signal
0044     //
0045     connect(ui->chkViCommandsOverride, &QCheckBox::toggled, this, &ConfigTab::slotChanged);
0046     connect(ui->chkViRelLineNumbers, &QCheckBox::toggled, this, &ConfigTab::slotChanged);
0047     connect(ui->tblNormalModeMappings, &QTableWidget::cellChanged, this, &ConfigTab::slotChanged);
0048     connect(ui->btnAddNewRow, &QPushButton::clicked, this, &ConfigTab::addMappingRow);
0049     connect(ui->btnAddNewRow, &QPushButton::clicked, this, &ConfigTab::slotChanged);
0050     connect(ui->btnRemoveSelectedRows, &QPushButton::clicked, this, &ConfigTab::removeSelectedMappingRows);
0051     connect(ui->btnRemoveSelectedRows, &QPushButton::clicked, this, &ConfigTab::slotChanged);
0052     connect(ui->btnImportNormal, &QPushButton::clicked, this, &ConfigTab::importNormalMappingRow);
0053     connect(ui->btnImportNormal, &QPushButton::clicked, this, &ConfigTab::slotChanged);
0054 
0055     layout->addWidget(newWidget);
0056 }
0057 
0058 ConfigTab::~ConfigTab()
0059 {
0060     delete ui;
0061 }
0062 
0063 void ConfigTab::applyTab(QTableWidget *mappingsTable, Mappings::MappingMode mode)
0064 {
0065     m_mappings->clear(mode);
0066 
0067     for (int i = 0; i < mappingsTable->rowCount(); i++) {
0068         QTableWidgetItem *from = mappingsTable->item(i, 0);
0069         QTableWidgetItem *to = mappingsTable->item(i, 1);
0070         QTableWidgetItem *recursive = mappingsTable->item(i, 2);
0071 
0072         if (from && to && recursive) {
0073             const Mappings::MappingRecursion recursion = recursive->checkState() == Qt::Checked ? Mappings::Recursive : Mappings::NonRecursive;
0074             m_mappings->add(mode, from->text(), to->text(), recursion);
0075         }
0076     }
0077 }
0078 
0079 void ConfigTab::reloadTab(QTableWidget *mappingsTable, Mappings::MappingMode mode)
0080 {
0081     const QStringList l = m_mappings->getAll(mode);
0082     mappingsTable->setRowCount(l.size());
0083 
0084     int i = 0;
0085     for (const QString &f : l) {
0086         QTableWidgetItem *from = new QTableWidgetItem(KeyParser::self()->decodeKeySequence(f));
0087         QString s = m_mappings->get(mode, f);
0088         QTableWidgetItem *to = new QTableWidgetItem(KeyParser::self()->decodeKeySequence(s));
0089         QTableWidgetItem *recursive = new QTableWidgetItem();
0090         recursive->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable);
0091         const bool isRecursive = m_mappings->isRecursive(mode, f);
0092         recursive->setCheckState(isRecursive ? Qt::Checked : Qt::Unchecked);
0093 
0094         mappingsTable->setItem(i, 0, from);
0095         mappingsTable->setItem(i, 1, to);
0096         mappingsTable->setItem(i, 2, recursive);
0097 
0098         i++;
0099     }
0100 }
0101 
0102 void ConfigTab::apply()
0103 {
0104     // nothing changed, no need to apply stuff
0105     if (!hasChanged()) {
0106         return;
0107     }
0108     m_changed = false;
0109 
0110     KateViewConfig::global()->configStart();
0111 
0112     // General options.
0113     KateViewConfig::global()->setValue(KateViewConfig::ViRelativeLineNumbers, ui->chkViRelLineNumbers->isChecked());
0114     KateViewConfig::global()->setValue(KateViewConfig::ViInputModeStealKeys, ui->chkViCommandsOverride->isChecked());
0115 
0116     // Mappings.
0117     applyTab(ui->tblNormalModeMappings, Mappings::NormalModeMapping);
0118     applyTab(ui->tblInsertModeMappings, Mappings::InsertModeMapping);
0119     applyTab(ui->tblVisualModeMappings, Mappings::VisualModeMapping);
0120 
0121     KateViewConfig::global()->configEnd();
0122 }
0123 
0124 void ConfigTab::reload()
0125 {
0126     // General options.
0127     ui->chkViRelLineNumbers->setChecked(KateViewConfig::global()->viRelativeLineNumbers());
0128     ui->chkViCommandsOverride->setChecked(KateViewConfig::global()->viInputModeStealKeys());
0129 
0130     // Mappings.
0131     reloadTab(ui->tblNormalModeMappings, Mappings::NormalModeMapping);
0132     reloadTab(ui->tblInsertModeMappings, Mappings::InsertModeMapping);
0133     reloadTab(ui->tblVisualModeMappings, Mappings::VisualModeMapping);
0134 }
0135 
0136 void ConfigTab::reset()
0137 {
0138     /* Do nothing. */
0139 }
0140 
0141 void ConfigTab::defaults()
0142 {
0143     /* Do nothing. */
0144 }
0145 
0146 void ConfigTab::showWhatsThis(const QString &text)
0147 {
0148     QWhatsThis::showText(QCursor::pos(), text);
0149 }
0150 
0151 void ConfigTab::addMappingRow()
0152 {
0153     // Pick the current widget.
0154     QTableWidget *mappingsTable = ui->tblNormalModeMappings;
0155     if (ui->tabMappingModes->currentIndex() == 1) {
0156         mappingsTable = ui->tblInsertModeMappings;
0157     } else if (ui->tabMappingModes->currentIndex() == 2) {
0158         mappingsTable = ui->tblVisualModeMappings;
0159     }
0160 
0161     // And add a new row.
0162     int rows = mappingsTable->rowCount();
0163     mappingsTable->insertRow(rows);
0164     QTableWidgetItem *recursive = new QTableWidgetItem();
0165     recursive->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable);
0166     recursive->setCheckState(Qt::Unchecked);
0167     mappingsTable->setItem(rows, 2, recursive);
0168     mappingsTable->setCurrentCell(rows, 0);
0169     mappingsTable->editItem(mappingsTable->currentItem());
0170 }
0171 
0172 void ConfigTab::removeSelectedMappingRows()
0173 {
0174     // Pick the current widget.
0175     QTableWidget *mappingsTable = ui->tblNormalModeMappings;
0176     if (ui->tabMappingModes->currentIndex() == 1) {
0177         mappingsTable = ui->tblInsertModeMappings;
0178     } else if (ui->tabMappingModes->currentIndex() == 2) {
0179         mappingsTable = ui->tblVisualModeMappings;
0180     }
0181 
0182     // And remove the selected rows.
0183     const QList<QTableWidgetSelectionRange> l = mappingsTable->selectedRanges();
0184     for (const QTableWidgetSelectionRange &range : l) {
0185         for (int i = 0; i < range.bottomRow() - range.topRow() + 1; i++) {
0186             mappingsTable->removeRow(range.topRow());
0187         }
0188     }
0189 }
0190 
0191 void ConfigTab::importNormalMappingRow()
0192 {
0193     const QString &fileName = QFileDialog::getOpenFileName(this);
0194 
0195     if (fileName.isEmpty()) {
0196         return;
0197     }
0198 
0199     QFile configFile(fileName);
0200     if (!configFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
0201         KMessageBox::error(this, i18n("Unable to open the config file for reading."), i18n("Unable to open file"));
0202         return;
0203     }
0204 
0205     QTextStream stream(&configFile);
0206     const QRegularExpression mapleader(QStringLiteral("(?:\\w:)?mapleader"));
0207     while (!stream.atEnd()) {
0208         const QStringList line = stream.readLine().split(QLatin1Char(' '));
0209 
0210         // TODO - allow recursive mappings to be read.
0211         if (line.size() > 2
0212             && (line[0] == QLatin1String("noremap") || line[0] == QLatin1String("no") || line[0] == QLatin1String("nnoremap")
0213                 || line[0] == QLatin1String("nn"))) {
0214             int rows = ui->tblNormalModeMappings->rowCount();
0215             ui->tblNormalModeMappings->insertRow(rows);
0216             ui->tblNormalModeMappings->setItem(rows, 0, new QTableWidgetItem(line[1]));
0217             ui->tblNormalModeMappings->setItem(rows, 1, new QTableWidgetItem(line[2]));
0218             QTableWidgetItem *recursive = new QTableWidgetItem();
0219             recursive->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable);
0220             recursive->setCheckState(Qt::Unchecked);
0221             ui->tblNormalModeMappings->setItem(rows, 2, recursive);
0222         } else if (line.size() == 4 && line[0] == QLatin1String("let") && line[2] == QLatin1String("=") && mapleader.match(line[1]).hasMatch()) {
0223             const QStringView leader = QStringView(line[3]).mid(1, line[3].length() - 2);
0224             if (!leader.isEmpty()) {
0225                 m_mappings->setLeader(leader[0]);
0226             }
0227         }
0228     }
0229 }
0230 
0231 QString ConfigTab::name() const
0232 {
0233     return i18n("Vi Input Mode");
0234 }