1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
|
#ifndef SQLEDITOR_H
#define SQLEDITOR_H
#include "guiSQLiteStudio_global.h"
#include "common/extactioncontainer.h"
#include "db/db.h"
#include "sqlitesyntaxhighlighter.h"
#include <QPlainTextEdit>
#include <QTextEdit>
#include <QFont>
#include <QHash>
class CompleterWindow;
class QTimer;
class Parser;
class SqlEditor;
class SearchTextDialog;
class SearchTextLocator;
CFG_KEY_LIST(SqlEditor, QObject::tr("SQL editor input field"),
CFG_KEY_ENTRY(CUT, QKeySequence::Cut, QObject::tr("Cut selected text"))
CFG_KEY_ENTRY(COPY, QKeySequence::Copy, QObject::tr("Copy selected text"))
CFG_KEY_ENTRY(PASTE, QKeySequence::Paste, QObject::tr("Paste from clipboard"))
CFG_KEY_ENTRY(DELETE, QKeySequence::Delete, QObject::tr("Delete selected text"))
CFG_KEY_ENTRY(SELECT_ALL, QKeySequence::SelectAll, QObject::tr("Select whole editor contents"))
CFG_KEY_ENTRY(UNDO, QKeySequence::Undo, QObject::tr("Undo"))
CFG_KEY_ENTRY(REDO, QKeySequence::Redo, QObject::tr("Redo"))
CFG_KEY_ENTRY(SAVE_SQL_FILE, QKeySequence::Save, QObject::tr("Save contents into a file"))
CFG_KEY_ENTRY(OPEN_SQL_FILE, QKeySequence::Open, QObject::tr("Load contents from a file"))
CFG_KEY_ENTRY(FIND, QKeySequence::Find, QObject::tr("Find in text"))
CFG_KEY_ENTRY(FIND_NEXT, QKeySequence::FindNext, QObject::tr("Find next"))
CFG_KEY_ENTRY(FIND_PREV, QKeySequence::FindPrevious, QObject::tr("Find previous"))
CFG_KEY_ENTRY(REPLACE, QKeySequence::Replace, QObject::tr("Replace in text"))
CFG_KEY_ENTRY(DELETE_LINE, Qt::CTRL + Qt::Key_D, QObject::tr("Delete current line"))
CFG_KEY_ENTRY(COMPLETE, Qt::CTRL + Qt::Key_Space, QObject::tr("Request code assistant"))
CFG_KEY_ENTRY(FORMAT_SQL, Qt::CTRL + Qt::Key_T, QObject::tr("Format contents"))
CFG_KEY_ENTRY(MOVE_BLOCK_DOWN, Qt::ALT + Qt::Key_Down, QObject::tr("Move selected block of text one line down"))
CFG_KEY_ENTRY(MOVE_BLOCK_UP, Qt::ALT + Qt::Key_Up, QObject::tr("Move selected block of text one line up"))
CFG_KEY_ENTRY(COPY_BLOCK_DOWN, Qt::ALT + Qt::CTRL + Qt::Key_Down, QObject::tr("Copy selected block of text and paste it a line below"))
CFG_KEY_ENTRY(COPY_BLOCK_UP, Qt::ALT + Qt::CTRL + Qt::Key_Up, QObject::tr("Copy selected block of text and paste it a line above"))
)
class GUI_API_EXPORT SqlEditor : public QPlainTextEdit, public ExtActionContainer
{
Q_OBJECT
Q_ENUMS(Action)
public:
enum Action
{
COPY,
PASTE,
CUT,
UNDO,
REDO,
DELETE,
SELECT_ALL,
FORMAT_SQL,
OPEN_SQL_FILE,
SAVE_SQL_FILE,
DELETE_LINE,
COMPLETE,
MOVE_BLOCK_DOWN,
MOVE_BLOCK_UP,
COPY_BLOCK_DOWN,
COPY_BLOCK_UP,
FIND,
FIND_NEXT,
FIND_PREV,
REPLACE
};
enum ToolBar
{
};
explicit SqlEditor(QWidget *parent = 0);
~SqlEditor();
Db* getDb() const;
void setDb(Db* value);
void setAutoCompletion(bool enabled);
QString getVirtualSqlExpression() const;
void setVirtualSqlExpression(const QString& value);
void setTriggerContext(const QString& table);
bool haveErrors();
bool isSyntaxChecked();
bool getShowLineNumbers() const;
void setShowLineNumbers(bool value);
void checkSyntaxNow();
void saveSelection();
void restoreSelection();
QToolBar* getToolBar(int toolbar) const;
bool getVirtualSqlCompleteSemicolon() const;
void setVirtualSqlCompleteSemicolon(bool value);
protected:
void setupDefShortcuts();
void createActions();
void keyPressEvent(QKeyEvent* e);
void keyReleaseEvent(QKeyEvent* e);
void focusOutEvent(QFocusEvent* e);
void focusInEvent(QFocusEvent* e);
void mouseMoveEvent(QMouseEvent* e);
void mousePressEvent(QMouseEvent* e);
void resizeEvent(QResizeEvent *e);
void changeEvent(QEvent*e);
private:
class LineNumberArea : public QWidget
{
public:
explicit LineNumberArea(SqlEditor *editor);
QSize sizeHint() const;
protected:
void paintEvent(QPaintEvent *event);
private:
SqlEditor *codeEditor = nullptr;
};
struct DbObject
{
DbObject(int from, int to, const QString& dbName);
int from;
int to;
/**
* @brief dbName
* Attach name for the db that object belongs to.
* If the object is database itself, then this variable is null.
*/
QString dbName;
};
void setupMenu();
void updateCompleterPosition();
void init();
void removeErrorMarkers();
void deleteCurrentLine();
void deleteSelectedLines();
/**
* @brief markErrorAt Mark error range.
* @param start Start index of error.
* @param end End index of error.
* @param limitedDamage true if error is just invalid token, that didn't cause parser to fail.
*/
void markErrorAt(int start, int end, bool limitedDamage = false);
void deletePreviousChars(int length = 1);
void refreshValidObjects();
void checkForSyntaxErrors();
void checkForValidObjects();
Dialect getDialect();
void setObjectLinks(bool enabled);
void addDbObject(int from, int to, const QString& dbName);
void clearDbObjects();
void lineNumberAreaPaintEvent(QPaintEvent* event);
int lineNumberAreaWidth();
void highlightParenthesis();
const TextBlockData::Parenthesis* matchParenthesis(QList<const TextBlockData::Parenthesis*> parList, const TextBlockData::Parenthesis* thePar);
void markMatchedParenthesis(int pos1, int pos2, QList<QTextEdit::ExtraSelection>& selections);
void doBackspace(int repeats = 1);
void indentSelected(bool shiftPressed);
void indentBlock(const QTextBlock& block);
void unindentBlock(const QTextBlock& block);
void indentNewLine();
void showSearchDialog();
int sqlIndex(int idx);
void updateLineNumberArea();
bool hasSelection() const;
void replaceSelectedText(const QString& newText);
QString getSelectedText() const;
void openObject(const QString& database, const QString& name);
/**
* @brief getValidObjectForPosition
* @param position Cursor text position determinated by Qt mouse event.
* @param movedLeft true if Qt moved cursor left from click point, which means that user clicked closer to left border of character. Otherwise cursor was moved towards right.
* @return Object identified under given text position, or null if there was no valid object under that position.
*/
const DbObject* getValidObjectForPosition(int position, bool movedLeft);
const DbObject* getValidObjectForPosition(const QPoint& point);
void handleValidObjectCursor(const QPoint& point);
SqliteSyntaxHighlighter* highlighter = nullptr;
QMenu* contextMenu = nullptr;
QMenu* validObjContextMenu = nullptr;
Db* db = nullptr;
CompleterWindow* completer = nullptr;
QTimer* autoCompleteTimer = nullptr;
bool autoCompletion = true;
bool deletionKeyPressed = false;
QTimer* queryParserTimer = nullptr;
Parser* queryParser = nullptr;
QHash<QString,QStringList> objectsInNamedDb;
bool objectLinksEnabled = false;
QList<DbObject> validDbObjects;
QWidget* lineNumberArea = nullptr;
SearchTextDialog* searchDialog = nullptr;
SearchTextLocator* textLocator = nullptr;
bool cursorMovingByLocator = false;
bool syntaxValidated = false;
bool showLineNumbers = true;
int storedSelectionStart = 0;
int storedSelectionEnd = 0;
bool richFeaturesEnabled = true;
/**
* @brief virtualSqlExpression
* It has to be an SQL string containing exactly one argument %1 (as Qt string arguments).
* It will be used in every syntax completion request as a template, as if user
* wrote this entire SQL statement, plus his own code in place of %1 and then the completer is invoked.
* User never sees this SQL expression, it's hidden from him.
* The expression will also be used for syntax error checking the same was as for completer.
*
* This is useful for example when we want to have a context for completion in CHECK() constraint,
* but user has only input edit for the CHECK expression itself, so for completer to work correctly
* it needs to be lied that there is entire "CREATE TABLE...CHECK(" before the users code. In that
* case you would set this variable to something like this: "CREATE TABLE x (c CHECK(%1))".
* The SqlEditor is smart enough to do all the magic given the above expression.
*/
QString virtualSqlExpression;
int virtualSqlOffset = 0;
int virtualSqlRightOffset = 0;
bool virtualSqlCompleteSemicolon = false;
QString createTriggerTable;
static const int autoCompleterDelay = 300;
static const int queryParserDelay = 500;
private slots:
void customContextMenuRequested(const QPoint& pos);
void updateUndoAction(bool enabled);
void updateRedoAction(bool enabled);
void updateCopyAction(bool enabled);
void deleteSelected();
void homePressed(Qt::KeyboardModifiers modifiers);
void tabPressed(bool shiftPressed);
void backspacePressed();
void complete();
void completeSelected();
void scheduleAutoCompletion();
void checkForAutoCompletion();
void completerTypedText(const QString& text);
void completerBackspacePressed();
void completerLeftPressed();
void completerRightPressed();
void parseContents();
void scheduleQueryParser(bool force = false);
void updateLineNumberAreaWidth();
void highlightCurrentLine();
void updateLineNumberArea(const QRect&rect, int dy);
void cursorMoved();
void formatSql();
void saveToFile();
void loadFromFile();
void deleteLine();
void moveBlockDown(bool deleteOld = true);
void moveBlockUp(bool deleteOld = true);
void copyBlockDown();
void copyBlockUp();
void find();
void findNext();
void findPrevious();
void replace();
void found(int start, int end);
void reachedEnd();
void changeFont(const QVariant& font);
void configModified();
signals:
void errorsChecked(bool haveErrors);
};
#endif // SQLEDITOR_H
|