1/*
2 SPDX-FileCopyrightText: KDE Developers
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include <QFileDialog>
8#include <QTableWidgetItem>
9#include <QWhatsThis>
10
11#include <KLocalizedString>
12#include <KMessageBox>
13
14#include "ui_configwidget.h"
15#include <utils/kateconfig.h>
16#include <utils/kateglobal.h>
17#include <vimode/config/configtab.h>
18#include <vimode/keyparser.h>
19
20using namespace KateVi;
21
22ConfigTab::ConfigTab(QWidget *parent, Mappings *mappings)
23 : KateConfigPage(parent)
24 , m_mappings(mappings)
25{
26 // This will let us have more separation between this page and
27 // the QTabWidget edge (ereslibre)
28 QVBoxLayout *layout = new QVBoxLayout(this);
29 QWidget *newWidget = new QWidget(this);
30
31 ui = new Ui::ConfigWidget();
32 ui->setupUi(newWidget);
33
34 // Make the header take all the width in equal parts.
35 ui->tblNormalModeMappings->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
36 ui->tblInsertModeMappings->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
37 ui->tblVisualModeMappings->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
38
39 // What's This? help can be found in the ui file
40 reload();
41
42 //
43 // after initial reload, connect the stuff for the changed() signal
44 //
45 connect(sender: ui->chkViCommandsOverride, signal: &QCheckBox::toggled, context: this, slot: &ConfigTab::slotChanged);
46 connect(sender: ui->chkViRelLineNumbers, signal: &QCheckBox::toggled, context: this, slot: &ConfigTab::slotChanged);
47 connect(sender: ui->tblNormalModeMappings, signal: &QTableWidget::cellChanged, context: this, slot: &ConfigTab::slotChanged);
48 connect(sender: ui->btnAddNewRow, signal: &QPushButton::clicked, context: this, slot: &ConfigTab::addMappingRow);
49 connect(sender: ui->btnAddNewRow, signal: &QPushButton::clicked, context: this, slot: &ConfigTab::slotChanged);
50 connect(sender: ui->btnRemoveSelectedRows, signal: &QPushButton::clicked, context: this, slot: &ConfigTab::removeSelectedMappingRows);
51 connect(sender: ui->btnRemoveSelectedRows, signal: &QPushButton::clicked, context: this, slot: &ConfigTab::slotChanged);
52 connect(sender: ui->btnImportNormal, signal: &QPushButton::clicked, context: this, slot: &ConfigTab::importNormalMappingRow);
53 connect(sender: ui->btnImportNormal, signal: &QPushButton::clicked, context: this, slot: &ConfigTab::slotChanged);
54
55 layout->addWidget(newWidget);
56}
57
58ConfigTab::~ConfigTab()
59{
60 delete ui;
61}
62
63void ConfigTab::applyTab(QTableWidget *mappingsTable, Mappings::MappingMode mode)
64{
65 m_mappings->clear(mode);
66
67 for (int i = 0; i < mappingsTable->rowCount(); i++) {
68 QTableWidgetItem *from = mappingsTable->item(row: i, column: 0);
69 QTableWidgetItem *to = mappingsTable->item(row: i, column: 1);
70 QTableWidgetItem *recursive = mappingsTable->item(row: i, column: 2);
71
72 if (from && to && recursive) {
73 const Mappings::MappingRecursion recursion = recursive->checkState() == Qt::Checked ? Mappings::Recursive : Mappings::NonRecursive;
74 m_mappings->add(mode, from: from->text(), to: to->text(), recursion);
75 }
76 }
77}
78
79void ConfigTab::reloadTab(QTableWidget *mappingsTable, Mappings::MappingMode mode)
80{
81 const QStringList l = m_mappings->getAll(mode);
82 mappingsTable->setRowCount(l.size());
83
84 int i = 0;
85 for (const QString &f : l) {
86 QTableWidgetItem *from = new QTableWidgetItem(KeyParser::self()->decodeKeySequence(keys: f));
87 QString s = m_mappings->get(mode, from: f);
88 QTableWidgetItem *to = new QTableWidgetItem(KeyParser::self()->decodeKeySequence(keys: s));
89 QTableWidgetItem *recursive = new QTableWidgetItem();
90 recursive->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable);
91 const bool isRecursive = m_mappings->isRecursive(mode, from: f);
92 recursive->setCheckState(isRecursive ? Qt::Checked : Qt::Unchecked);
93
94 mappingsTable->setItem(row: i, column: 0, item: from);
95 mappingsTable->setItem(row: i, column: 1, item: to);
96 mappingsTable->setItem(row: i, column: 2, item: recursive);
97
98 i++;
99 }
100}
101
102void ConfigTab::apply()
103{
104 // nothing changed, no need to apply stuff
105 if (!hasChanged()) {
106 return;
107 }
108 m_changed = false;
109
110 KateViewConfig::global()->configStart();
111
112 // General options.
113 KateViewConfig::global()->setValue(key: KateViewConfig::ViRelativeLineNumbers, value: ui->chkViRelLineNumbers->isChecked());
114 KateViewConfig::global()->setValue(key: KateViewConfig::ViInputModeStealKeys, value: ui->chkViCommandsOverride->isChecked());
115
116 // Mappings.
117 applyTab(mappingsTable: ui->tblNormalModeMappings, mode: Mappings::NormalModeMapping);
118 applyTab(mappingsTable: ui->tblInsertModeMappings, mode: Mappings::InsertModeMapping);
119 applyTab(mappingsTable: ui->tblVisualModeMappings, mode: Mappings::VisualModeMapping);
120
121 KateViewConfig::global()->configEnd();
122}
123
124void ConfigTab::reload()
125{
126 // General options.
127 ui->chkViRelLineNumbers->setChecked(KateViewConfig::global()->viRelativeLineNumbers());
128 ui->chkViCommandsOverride->setChecked(KateViewConfig::global()->viInputModeStealKeys());
129
130 // Mappings.
131 reloadTab(mappingsTable: ui->tblNormalModeMappings, mode: Mappings::NormalModeMapping);
132 reloadTab(mappingsTable: ui->tblInsertModeMappings, mode: Mappings::InsertModeMapping);
133 reloadTab(mappingsTable: ui->tblVisualModeMappings, mode: Mappings::VisualModeMapping);
134}
135
136void ConfigTab::reset()
137{
138 /* Do nothing. */
139}
140
141void ConfigTab::defaults()
142{
143 /* Do nothing. */
144}
145
146void ConfigTab::showWhatsThis(const QString &text)
147{
148 QWhatsThis::showText(pos: QCursor::pos(), text);
149}
150
151void ConfigTab::addMappingRow()
152{
153 // Pick the current widget.
154 QTableWidget *mappingsTable = ui->tblNormalModeMappings;
155 if (ui->tabMappingModes->currentIndex() == 1) {
156 mappingsTable = ui->tblInsertModeMappings;
157 } else if (ui->tabMappingModes->currentIndex() == 2) {
158 mappingsTable = ui->tblVisualModeMappings;
159 }
160
161 // And add a new row.
162 int rows = mappingsTable->rowCount();
163 mappingsTable->insertRow(row: rows);
164 QTableWidgetItem *recursive = new QTableWidgetItem();
165 recursive->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable);
166 recursive->setCheckState(Qt::Unchecked);
167 mappingsTable->setItem(row: rows, column: 2, item: recursive);
168 mappingsTable->setCurrentCell(row: rows, column: 0);
169 mappingsTable->editItem(item: mappingsTable->currentItem());
170}
171
172void ConfigTab::removeSelectedMappingRows()
173{
174 // Pick the current widget.
175 QTableWidget *mappingsTable = ui->tblNormalModeMappings;
176 if (ui->tabMappingModes->currentIndex() == 1) {
177 mappingsTable = ui->tblInsertModeMappings;
178 } else if (ui->tabMappingModes->currentIndex() == 2) {
179 mappingsTable = ui->tblVisualModeMappings;
180 }
181
182 // And remove the selected rows.
183 const QList<QTableWidgetSelectionRange> l = mappingsTable->selectedRanges();
184 for (const QTableWidgetSelectionRange &range : l) {
185 for (int i = 0; i < range.bottomRow() - range.topRow() + 1; i++) {
186 mappingsTable->removeRow(row: range.topRow());
187 }
188 }
189}
190
191void ConfigTab::importNormalMappingRow()
192{
193 const QString &fileName = QFileDialog::getOpenFileName(parent: this);
194
195 if (fileName.isEmpty()) {
196 return;
197 }
198
199 QFile configFile(fileName);
200 if (!configFile.open(flags: QIODevice::ReadOnly | QIODevice::Text)) {
201 KMessageBox::error(parent: this, i18n("Unable to open the config file for reading."), i18n("Unable to open file"));
202 return;
203 }
204
205 QTextStream stream(&configFile);
206 const QRegularExpression mapleader(QStringLiteral("(?:\\w:)?mapleader"));
207 while (!stream.atEnd()) {
208 const QStringList line = stream.readLine().split(sep: QLatin1Char(' '));
209
210 // TODO - allow recursive mappings to be read.
211 if (line.size() > 2
212 && (line[0] == QLatin1String("noremap") || line[0] == QLatin1String("no") || line[0] == QLatin1String("nnoremap")
213 || line[0] == QLatin1String("nn"))) {
214 int rows = ui->tblNormalModeMappings->rowCount();
215 ui->tblNormalModeMappings->insertRow(row: rows);
216 ui->tblNormalModeMappings->setItem(row: rows, column: 0, item: new QTableWidgetItem(line[1]));
217 ui->tblNormalModeMappings->setItem(row: rows, column: 1, item: new QTableWidgetItem(line[2]));
218 QTableWidgetItem *recursive = new QTableWidgetItem();
219 recursive->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable);
220 recursive->setCheckState(Qt::Unchecked);
221 ui->tblNormalModeMappings->setItem(row: rows, column: 2, item: recursive);
222 } else if (line.size() == 4 && line[0] == QLatin1String("let") && line[2] == QLatin1String("=") && mapleader.match(subject: line[1]).hasMatch()) {
223 const QStringView leader = QStringView(line[3]).mid(pos: 1, n: line[3].length() - 2);
224 if (!leader.isEmpty()) {
225 m_mappings->setLeader(leader[0]);
226 }
227 }
228 }
229}
230
231QString ConfigTab::name() const
232{
233 return i18n("Vi Input Mode");
234}
235

source code of ktexteditor/src/vimode/config/configtab.cpp