Skip to content

Commit 8896ecc

Browse files
committed
Refactoring and find/replace dialog in the JSON editor.
SQL and JSON text editor classes have been refactored. A new parent class for both editors have been added for the common logic implementable without depending on the specific lexer. The only visible effect of this change should be that the JSON editor (issue #1173) now has the same find/replace dialog as the SQL editor. This prepares for the implementation of the XML editor (issue #1253).
1 parent de260d3 commit 8896ecc

10 files changed

Lines changed: 308 additions & 405 deletions

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ set(SQLB_MOC_HDR
121121
src/RemoteModel.h
122122
src/RemotePushDialog.h
123123
src/FindReplaceDialog.h
124+
src/ExtendedScintilla.h
124125
)
125126

126127
set(SQLB_SRC
@@ -161,6 +162,7 @@ set(SQLB_SRC
161162
src/RemoteModel.cpp
162163
src/RemotePushDialog.cpp
163164
src/FindReplaceDialog.cpp
165+
src/ExtendedScintilla.cpp
164166
)
165167

166168
set(SQLB_FORMS

src/ExtendedScintilla.cpp

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
#include "ExtendedScintilla.h"
2+
#include "FindReplaceDialog.h"
3+
#include "Settings.h"
4+
#include "Qsci/qscilexer.h"
5+
6+
#include <QFile>
7+
#include <QDropEvent>
8+
#include <QUrl>
9+
#include <QMimeData>
10+
#include <QShortcut>
11+
#include <QAction>
12+
#include <QMenu>
13+
#include <cmath>
14+
15+
16+
ExtendedScintilla::ExtendedScintilla(QWidget* parent) :
17+
QsciScintilla(parent),
18+
findReplaceDialog(new FindReplaceDialog(this))
19+
{
20+
// This class does not set any lexer, that must be done in the child classes.
21+
22+
// Enable UTF8
23+
setUtf8(true);
24+
25+
// Enable brace matching
26+
setBraceMatching(QsciScintilla::SloppyBraceMatch);
27+
28+
// Enable auto indentation
29+
setAutoIndent(true);
30+
31+
// Enable folding
32+
setFolding(QsciScintilla::BoxedTreeFoldStyle);
33+
34+
// Create error indicator
35+
errorIndicatorNumber = indicatorDefine(QsciScintilla::SquiggleIndicator);
36+
setIndicatorForegroundColor(Qt::red, errorIndicatorNumber);
37+
38+
// Set a sensible scroll width, so the scroll bar is avoided in
39+
// most cases.
40+
setScrollWidth(80);
41+
42+
// Scroll width is adjusted to ensure that all of the lines
43+
// currently displayed can be completely scrolled. This mode never
44+
// adjusts the scroll width to be narrower.
45+
setScrollWidthTracking(true);
46+
47+
// Connect signals
48+
connect(this, SIGNAL(linesChanged()), this, SLOT(updateLineNumberAreaWidth()));
49+
50+
// The shortcut is constrained to the Widget context so it does not conflict with other SqlTextEdit widgets in the Main Window.
51+
QShortcut* shortcutFindReplace = new QShortcut(QKeySequence(tr("Ctrl+H")), this, nullptr, nullptr, Qt::WidgetShortcut);
52+
connect(shortcutFindReplace, SIGNAL(activated()), this, SLOT(openFindReplaceDialog()));
53+
54+
// Prepare for adding the find/replace option to the QScintilla context menu
55+
setContextMenuPolicy(Qt::CustomContextMenu);
56+
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint &)));
57+
}
58+
59+
ExtendedScintilla::~ExtendedScintilla()
60+
{
61+
}
62+
63+
void ExtendedScintilla::updateLineNumberAreaWidth()
64+
{
65+
// Calculate number of digits of the current number of lines
66+
int digits = std::floor(std::log10(lines())) + 1;
67+
68+
// Calculate the width of this number if it was all zeros (this is because a 1 might require less space than a 0 and this could
69+
// cause some flickering depending on the font) and set the new margin width.
70+
QFont font = lexer()->defaultFont();
71+
setMarginWidth(0, QFontMetrics(font).width(QString("0").repeated(digits)) + 5);
72+
}
73+
74+
void ExtendedScintilla::dropEvent(QDropEvent* e)
75+
{
76+
QList<QUrl> urls = e->mimeData()->urls();
77+
if(urls.isEmpty())
78+
return QsciScintilla::dropEvent(e);
79+
80+
QString file = urls.first().toLocalFile();
81+
if(!QFile::exists(file))
82+
return;
83+
84+
QFile f(file);
85+
f.open(QIODevice::ReadOnly);
86+
setText(f.readAll());
87+
f.close();
88+
}
89+
90+
void ExtendedScintilla::setupSyntaxHighlightingFormat(const QString& settings_name, int style)
91+
{
92+
lexer()->setColor(QColor(Settings::getValue("syntaxhighlighter", settings_name + "_colour").toString()), style);
93+
94+
QFont font(Settings::getValue("editor", "font").toString());
95+
font.setPointSize(Settings::getValue("editor", "fontsize").toInt());
96+
font.setBold(Settings::getValue("syntaxhighlighter", settings_name + "_bold").toBool());
97+
font.setItalic(Settings::getValue("syntaxhighlighter", settings_name + "_italic").toBool());
98+
font.setUnderline(Settings::getValue("syntaxhighlighter", settings_name + "_underline").toBool());
99+
lexer()->setFont(font, style);
100+
}
101+
102+
void ExtendedScintilla::reloadKeywords()
103+
{
104+
// Set lexer again to reload the updated keywords list
105+
setLexer(lexer());
106+
}
107+
108+
void ExtendedScintilla::reloadSettings()
109+
{
110+
// Enable auto completion if it hasn't been disabled
111+
if(Settings::getValue("editor", "auto_completion").toBool())
112+
{
113+
setAutoCompletionThreshold(3);
114+
setAutoCompletionCaseSensitivity(true);
115+
setAutoCompletionShowSingle(true);
116+
setAutoCompletionSource(QsciScintilla::AcsAPIs);
117+
} else {
118+
setAutoCompletionThreshold(0);
119+
}
120+
121+
// Set syntax highlighting settings
122+
QFont defaultfont(Settings::getValue("editor", "font").toString());
123+
defaultfont.setStyleHint(QFont::TypeWriter);
124+
defaultfont.setPointSize(Settings::getValue("editor", "fontsize").toInt());
125+
lexer()->setDefaultColor(Qt::black);
126+
lexer()->setFont(defaultfont);
127+
128+
// Set font
129+
QFont font(Settings::getValue("editor", "font").toString());
130+
font.setStyleHint(QFont::TypeWriter);
131+
font.setPointSize(Settings::getValue("editor", "fontsize").toInt());
132+
setFont(font);
133+
134+
// Show line numbers
135+
QFont marginsfont(QFont(Settings::getValue("editor", "font").toString()));
136+
marginsfont.setPointSize(font.pointSize());
137+
setMarginsFont(marginsfont);
138+
setMarginLineNumbers(0, true);
139+
setMarginsBackgroundColor(Qt::lightGray);
140+
updateLineNumberAreaWidth();
141+
142+
// Highlight current line
143+
setCaretLineVisible(true);
144+
setCaretLineBackgroundColor(QColor(Settings::getValue("syntaxhighlighter", "currentline_colour").toString()));
145+
146+
// Set tab width
147+
setTabWidth(Settings::getValue("editor", "tabsize").toInt());
148+
lexer()->refreshProperties();
149+
150+
// Check if error indicators are enabled and clear them if they just got disabled
151+
showErrorIndicators = Settings::getValue("editor", "error_indicators").toBool();
152+
if(!showErrorIndicators)
153+
clearErrorIndicators();
154+
155+
}
156+
157+
void ExtendedScintilla::clearErrorIndicators()
158+
{
159+
// Clear any error indicators from position (0,0) to the last column of the last line
160+
clearIndicatorRange(0, 0, lines(), lineLength(lines()), errorIndicatorNumber);
161+
}
162+
163+
void ExtendedScintilla::setErrorIndicator(int fromRow, int fromIndex, int toRow, int toIndex)
164+
{
165+
// Set error indicator for the specified range but only if they're enabled
166+
if(showErrorIndicators)
167+
fillIndicatorRange(fromRow, fromIndex, toRow, toIndex, errorIndicatorNumber);
168+
}
169+
170+
void ExtendedScintilla::setErrorIndicator(int position)
171+
{
172+
// Set error indicator for the position until end of line, but only if they're enabled
173+
if(showErrorIndicators) {
174+
175+
int column = SendScintilla(QsciScintillaBase::SCI_GETCOLUMN, position);
176+
int line = SendScintilla(QsciScintillaBase::SCI_LINEFROMPOSITION, position);
177+
178+
fillIndicatorRange(line, column, line+1, 0, errorIndicatorNumber);
179+
}
180+
}
181+
182+
bool ExtendedScintilla::findText(QString text, bool regexp, bool caseSensitive, bool words, bool wrap, bool forward) {
183+
184+
// For finding the previous occurrence, we need to skip the current
185+
// selection, otherwise we'd always found the same occurrence.
186+
if (!forward && hasSelectedText()) {
187+
int lineFrom, indexFrom;
188+
int lineTo, indexTo;
189+
getSelection(&lineFrom, &indexFrom, &lineTo, &indexTo);
190+
setCursorPosition(lineFrom, indexFrom);
191+
}
192+
193+
return findFirst(text, regexp, caseSensitive, words, wrap, forward);
194+
}
195+
196+
void ExtendedScintilla::clearSelection()
197+
{
198+
setSelection(-1,-1,-1,-1);
199+
}
200+
201+
void ExtendedScintilla::openFindReplaceDialog()
202+
{
203+
findReplaceDialog->setExtendedScintilla(this);
204+
findReplaceDialog->show();
205+
}
206+
207+
void ExtendedScintilla::showContextMenu(const QPoint &pos)
208+
{
209+
210+
QAction* findReplaceAction = new QAction(QIcon(":/icons/text_replace"), tr("Find and Replace..."), this);
211+
findReplaceAction->setShortcut(QKeySequence(tr("Ctrl+H")));
212+
connect(findReplaceAction, &QAction::triggered, [&]() {
213+
openFindReplaceDialog();
214+
});
215+
216+
// This has to be created here, otherwise the set of enabled options would not update accordingly.
217+
QMenu* editContextMenu = createStandardContextMenu();
218+
editContextMenu->addSeparator();
219+
editContextMenu->addAction(findReplaceAction);
220+
221+
editContextMenu->exec(mapToGlobal(pos));
222+
}

src/ExtendedScintilla.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#ifndef EXTENDEDSCINTILLA_H
2+
#define EXTENDEDSCINTILLA_H
3+
4+
#include "Qsci/qsciscintilla.h"
5+
6+
class FindReplaceDialog;
7+
8+
/**
9+
* @brief The ExtendedScintilla class
10+
* This class extends the QScintilla widget for the application
11+
*/
12+
class ExtendedScintilla : public QsciScintilla
13+
{
14+
Q_OBJECT
15+
16+
public:
17+
explicit ExtendedScintilla(QWidget *parent = nullptr);
18+
virtual ~ExtendedScintilla();
19+
20+
bool findText(QString text, bool regexp, bool caseSensitive, bool words, bool wrap, bool forward);
21+
void clearSelection();
22+
23+
public slots:
24+
void reloadKeywords();
25+
void reloadSettings();
26+
void clearErrorIndicators();
27+
void setErrorIndicator(int fromRow, int fromIndex, int toRow, int toIndex);
28+
// Set error indicator from position to end of line
29+
void setErrorIndicator(int position);
30+
void openFindReplaceDialog();
31+
32+
protected:
33+
void dropEvent(QDropEvent* e);
34+
35+
void setupSyntaxHighlightingFormat(const QString& settings_name, int style);
36+
37+
int errorIndicatorNumber;
38+
bool showErrorIndicators;
39+
FindReplaceDialog* findReplaceDialog;
40+
41+
private slots:
42+
void updateLineNumberAreaWidth();
43+
void showContextMenu(const QPoint &pos);
44+
};
45+
46+
#endif

src/FindReplaceDialog.cpp

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,28 @@ FindReplaceDialog::~FindReplaceDialog()
1717
delete ui;
1818
}
1919

20-
void FindReplaceDialog::setSqlTextEdit(SqlTextEdit* sqlTextEdit)
20+
void FindReplaceDialog::setExtendedScintilla(ExtendedScintilla* scintilla)
2121
{
22-
m_sqlTextEdit = sqlTextEdit;
22+
m_scintilla = scintilla;
2323

2424
// Create indicator for find-all and replace-all occurrences
25-
foundIndicatorNumber = m_sqlTextEdit->indicatorDefine(QsciScintilla::StraightBoxIndicator);
26-
m_sqlTextEdit->setIndicatorForegroundColor(Qt::magenta, foundIndicatorNumber);
27-
m_sqlTextEdit->setIndicatorDrawUnder(true, foundIndicatorNumber);
25+
foundIndicatorNumber = m_scintilla->indicatorDefine(QsciScintilla::StraightBoxIndicator);
26+
m_scintilla->setIndicatorForegroundColor(Qt::magenta, foundIndicatorNumber);
27+
m_scintilla->setIndicatorDrawUnder(true, foundIndicatorNumber);
2828

29-
bool isWriteable = ! m_sqlTextEdit->isReadOnly();
29+
bool isWriteable = ! m_scintilla->isReadOnly();
3030
ui->replaceWithText->setEnabled(isWriteable);
3131
ui->replaceButton->setEnabled(isWriteable);
3232
ui->replaceAllButton->setEnabled(isWriteable);
3333

34-
connect(m_sqlTextEdit, SIGNAL(destroyed()), this, SLOT(hide()));
34+
connect(m_scintilla, SIGNAL(destroyed()), this, SLOT(hide()));
3535
}
3636

3737
bool FindReplaceDialog::findNext()
3838
{
3939
clearIndicators();
4040

41-
bool found = m_sqlTextEdit->findText
41+
bool found = m_scintilla->findText
4242
(ui->findText->text(),
4343
ui->regexpCheckBox->isChecked(),
4444
ui->caseCheckBox->isChecked(),
@@ -61,23 +61,23 @@ void FindReplaceDialog::show()
6161

6262
void FindReplaceDialog::replace()
6363
{
64-
m_sqlTextEdit->replace(ui->replaceWithText->text());
64+
m_scintilla->replace(ui->replaceWithText->text());
6565
findNext();
6666
}
6767

6868
void FindReplaceDialog::indicateSelection()
6969
{
7070
int fromRow, fromIndex, toRow, toIndex;
71-
m_sqlTextEdit->getSelection(&fromRow, &fromIndex, &toRow, &toIndex);
72-
m_sqlTextEdit->fillIndicatorRange(fromRow, fromIndex, toRow, toIndex, foundIndicatorNumber);
71+
m_scintilla->getSelection(&fromRow, &fromIndex, &toRow, &toIndex);
72+
m_scintilla->fillIndicatorRange(fromRow, fromIndex, toRow, toIndex, foundIndicatorNumber);
7373

7474
}
7575
void FindReplaceDialog::findAll()
7676
{
7777
clearIndicators();
7878
int occurrences = 0;
79-
m_sqlTextEdit->setCursorPosition(0, 0);
80-
while (m_sqlTextEdit->findText
79+
m_scintilla->setCursorPosition(0, 0);
80+
while (m_scintilla->findText
8181
(ui->findText->text(),
8282
ui->regexpCheckBox->isChecked(),
8383
ui->caseCheckBox->isChecked(),
@@ -87,7 +87,7 @@ void FindReplaceDialog::findAll()
8787
indicateSelection();
8888
++occurrences;
8989
}
90-
m_sqlTextEdit->clearSelection();
90+
m_scintilla->clearSelection();
9191

9292
QString message;
9393
switch (occurrences) {
@@ -109,19 +109,19 @@ void FindReplaceDialog::replaceAll()
109109
{
110110
clearIndicators();
111111
int occurrences = 0;
112-
m_sqlTextEdit->setCursorPosition(0, 0);
113-
while (m_sqlTextEdit->findText
112+
m_scintilla->setCursorPosition(0, 0);
113+
while (m_scintilla->findText
114114
(ui->findText->text(),
115115
ui->regexpCheckBox->isChecked(),
116116
ui->caseCheckBox->isChecked(),
117117
ui->wholeWordsCheckBox->isChecked(),
118118
false,
119119
true)) {
120-
m_sqlTextEdit->replace(ui->replaceWithText->text());
120+
m_scintilla->replace(ui->replaceWithText->text());
121121
indicateSelection();
122122
++occurrences;
123123
}
124-
m_sqlTextEdit->clearSelection();
124+
m_scintilla->clearSelection();
125125

126126
QString message;
127127
switch (occurrences) {
@@ -147,12 +147,13 @@ void FindReplaceDialog::help()
147147

148148
void FindReplaceDialog::clearIndicators()
149149
{
150-
m_sqlTextEdit->clearIndicatorRange(0, 0, m_sqlTextEdit->lines(), m_sqlTextEdit->lineLength(m_sqlTextEdit->lines()), foundIndicatorNumber);
150+
m_scintilla->clearIndicatorRange(0, 0, m_scintilla->lines(), m_scintilla->lineLength(m_scintilla->lines()), foundIndicatorNumber);
151151
ui->messageLabel->setText("");
152152
}
153153

154154
void FindReplaceDialog::close()
155155
{
156+
m_scintilla->clearSelection();
156157
clearIndicators();
157158
QDialog::close();
158159
}

0 commit comments

Comments
 (0)