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