1 /* 2 * Copyright (C) Pedram Pourang (aka Tsu Jan) 2014-2021 <tsujan2000@gmail.com> 3 * 4 * FeatherPad is free software: you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License as published by the 6 * Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * FeatherPad is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 * See the GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License along 15 * with this program. If not, see <http://www.gnu.org/licenses/>. 16 * 17 * @license GPL-3.0+ <https://spdx.org/licenses/GPL-3.0+.html> 18 */ 19 20 #ifndef TEXTEDIT_H 21 #define TEXTEDIT_H 22 23 #include <QPlainTextEdit> 24 #include <QUrl> 25 #include <QMimeData> 26 #include <QDateTime> 27 #include <QElapsedTimer> 28 #include <QSyntaxHighlighter> 29 30 namespace FeatherPad { 31 32 /* This is for auto-indentation, line numbers, DnD, zooming, customized 33 vertical scrollbar, appropriate signals, and saving/getting useful info. */ 34 class TextEdit : public QPlainTextEdit 35 { 36 Q_OBJECT 37 38 public: 39 TextEdit (QWidget *parent = nullptr, int bgColorValue = 255); 40 ~TextEdit(); 41 setTextCursor(const QTextCursor & cursor)42 void setTextCursor (const QTextCursor &cursor) 43 { 44 QPlainTextEdit::setTextCursor (cursor); 45 /* this is needed for formatTextRect() to be called (for syntax highlighting) */ 46 emit QPlainTextEdit::updateRequest (rect(), 1); 47 } 48 49 void setEditorFont (const QFont &f, bool setDefault = true); 50 void adjustScrollbars(); 51 52 void lineNumberAreaPaintEvent (QPaintEvent *event); 53 int lineNumberAreaWidth(); 54 void showLineNumbers (bool show); 55 56 void sortLines (bool reverse = false); 57 58 bool toSoftTabs(); 59 60 QString getUrl (const int pos) const; 61 getDefaultFont()62 QFont getDefaultFont() const { 63 return font_; 64 } 65 getTextTab_()66 QString getTextTab_() const { 67 return textTab_; 68 } setTtextTab(int textTabSize)69 void setTtextTab (int textTabSize) { 70 textTab_ = textTab_.leftJustified (textTabSize, ' ', true); 71 } 72 currentLineSelection()73 QTextEdit::ExtraSelection currentLineSelection() { 74 return currentLine_; 75 } 76 setAutoIndentation(bool indent)77 void setAutoIndentation (bool indent) { 78 autoIndentation_ = indent; 79 } getAutoIndentation()80 bool getAutoIndentation() const { 81 return autoIndentation_; 82 } 83 setAutoReplace(bool replace)84 void setAutoReplace (bool replace) { 85 autoReplace_ = replace; 86 } getAutoReplace()87 bool getAutoReplace() const { 88 return autoReplace_; 89 } 90 setDrawIndetLines(bool draw)91 void setDrawIndetLines (bool draw) { 92 drawIndetLines_ = draw; 93 } 94 setVLineDistance(int distance)95 void setVLineDistance (int distance) { 96 vLineDistance_ = distance; 97 } 98 setDateFormat(const QString & format)99 void setDateFormat (const QString &format) { 100 dateFormat_ = format; 101 } 102 setAutoBracket(bool autoB)103 void setAutoBracket (bool autoB) { 104 autoBracket_ = autoB; 105 } 106 hasDarkScheme()107 bool hasDarkScheme() const { 108 return (darkValue_ > -1); 109 } getDarkValue()110 int getDarkValue() const { 111 return darkValue_; 112 } 113 getTextPrintColor()114 QColor getTextPrintColor() const { 115 /* with syntax highlighting, the color of line/document ends 116 should be returned because the ordinary text is formatted */ 117 if (highlighter_) 118 return separatorColor_; 119 return (darkValue_ == -1 ? Qt::black : Qt::white); 120 } 121 122 void setCurLineHighlight (int value); 123 124 void zooming (float range); 125 getSize()126 qint64 getSize() const { 127 return size_; 128 } setSize(qint64 size)129 void setSize (qint64 size) { 130 size_ = size; 131 } 132 getLastModified()133 QDateTime getLastModified() const { 134 return lastModified_; 135 } setLastModified(const QDateTime & m)136 void setLastModified (const QDateTime& m) { 137 lastModified_ = m; 138 } 139 getWordNumber()140 int getWordNumber() const { 141 return wordNumber_; 142 } setWordNumber(int n)143 void setWordNumber (int n) { 144 wordNumber_ = n; 145 } 146 getSearchedText()147 QString getSearchedText() const { 148 return searchedText_; 149 } setSearchedText(const QString & text)150 void setSearchedText (const QString &text) { 151 searchedText_ = text; 152 } 153 getReplaceTitle()154 QString getReplaceTitle() const { 155 return replaceTitle_; 156 } setReplaceTitle(const QString & title)157 void setReplaceTitle (const QString &title) { 158 replaceTitle_ = title; 159 } 160 getFileName()161 QString getFileName() const { 162 return fileName_; 163 } setFileName(const QString & name)164 void setFileName (const QString &name) { 165 fileName_ = name; 166 } 167 getProg()168 QString getProg() const { 169 return prog_.isEmpty() ? "url" // impossible; just a precaution 170 : prog_; 171 } setProg(const QString & prog)172 void setProg (const QString &prog) { 173 prog_ = prog; 174 } 175 getLang()176 QString getLang() const { 177 return lang_; 178 } setLang(const QString & lang)179 void setLang (const QString &lang) { 180 lang_ = lang; 181 } 182 getEncoding()183 QString getEncoding() const { 184 return encoding_; 185 } setEncoding(const QString & encoding)186 void setEncoding (const QString &encoding) { 187 encoding_ = encoding; 188 } 189 getGreenSel()190 QList<QTextEdit::ExtraSelection> getGreenSel() const { 191 return greenSel_; 192 } setGreenSel(QList<QTextEdit::ExtraSelection> sel)193 void setGreenSel (QList<QTextEdit::ExtraSelection> sel) { 194 greenSel_ = sel; 195 } 196 getRedSel()197 QList<QTextEdit::ExtraSelection> getRedSel() const { 198 return redSel_; 199 } setRedSel(QList<QTextEdit::ExtraSelection> sel)200 void setRedSel (QList<QTextEdit::ExtraSelection> sel) { 201 redSel_ = sel; 202 } 203 getBlueSel()204 QList<QTextEdit::ExtraSelection> getBlueSel() const { 205 return blueSel_; 206 } 207 isUneditable()208 bool isUneditable() const { 209 return uneditable_; 210 } makeUneditable(bool readOnly)211 void makeUneditable (bool readOnly) { 212 uneditable_ = readOnly; 213 } 214 getHighlighter()215 QSyntaxHighlighter *getHighlighter() const { 216 return highlighter_; 217 } setHighlighter(QSyntaxHighlighter * h)218 void setHighlighter (QSyntaxHighlighter *h) { 219 highlighter_ = h; 220 matchedBrackets_ = false; 221 } 222 getInertialScrolling()223 bool getInertialScrolling() const { 224 return inertialScrolling_; 225 } setInertialScrolling(bool inertial)226 void setInertialScrolling (bool inertial) { 227 inertialScrolling_ = inertial; 228 } 229 getSaveCursor()230 bool getSaveCursor() const { 231 return saveCursor_; 232 } setSaveCursor(bool save)233 void setSaveCursor (bool save) { 234 saveCursor_ = save; 235 } 236 getThickCursor()237 bool getThickCursor() const { 238 return (cursorWidth() > 1); 239 } setThickCursor(bool thick)240 void setThickCursor (bool thick) { 241 setCursorWidth (thick ? 2 : 1); 242 } 243 matchedBrackets()244 void matchedBrackets() { 245 matchedBrackets_ = true; 246 } 247 forgetTxtCurHPos()248 void forgetTxtCurHPos() { 249 keepTxtCurHPos_ = false; 250 txtCurHPos_ = -1; 251 } 252 getSelectionHighlighting()253 bool getSelectionHighlighting() const { 254 return selectionHighlighting_; 255 } 256 void setSelectionHighlighting (bool enable); 257 skipSelectionHighlighting()258 void skipSelectionHighlighting() { 259 highlightThisSelection_ = false; 260 } 261 setPastePaths(bool pastePaths)262 void setPastePaths (bool pastePaths) { 263 pastePaths_ = pastePaths; 264 } 265 266 QTextCursor finding (const QString& str, const QTextCursor& start, 267 QTextDocument::FindFlags flags = QTextDocument::FindFlags(), 268 bool isRegex = false, const int end = 0) const; 269 270 signals: 271 /* inform the main widget */ 272 void fileDropped (const QString& localFile, 273 int restoreCursor, // Only for connecting to FPwin::newTabFromName(). 274 int posInLine, // Only for connecting to FPwin::newTabFromName(). 275 bool multiple); // Multiple files are dropped? 276 void resized(); // needed by syntax highlighting 277 void updateRect(); 278 void zoomedOut (TextEdit *textEdit); // needed for reformatting text 279 void updateBracketMatching(); 280 281 public slots: 282 void copy(); 283 void cut(); 284 void undo(); 285 void redo(); 286 void paste(); 287 void selectAll(); 288 void insertPlainText (const QString &text); 289 void selectionHlight(); 290 void onContentsChange (int position, int charsRemoved, int charsAdded); 291 292 protected: 293 void keyPressEvent (QKeyEvent *event); 294 void keyReleaseEvent (QKeyEvent *event); 295 void wheelEvent (QWheelEvent *event); 296 void resizeEvent (QResizeEvent *event); 297 void timerEvent (QTimerEvent *event); 298 void paintEvent (QPaintEvent *event); // only for working around the RTL bug 299 void showEvent (QShowEvent *event); 300 void mouseMoveEvent (QMouseEvent *event); 301 void mousePressEvent (QMouseEvent *event); 302 void mouseReleaseEvent (QMouseEvent *event); 303 void mouseDoubleClickEvent (QMouseEvent *event); 304 bool event (QEvent *event); 305 bool eventFilter (QObject *watched, QEvent *event); 306 307 QMimeData* createMimeDataFromSelection() const; 308 /* we want to pass dropping of files to 309 the main widget with a custom signal */ 310 bool canInsertFromMimeData (const QMimeData* source) const; 311 void insertFromMimeData (const QMimeData* source); 312 313 private slots: 314 void updateLineNumberAreaWidth (int newBlockCount); 315 void highlightCurrentLine(); 316 void updateLineNumberArea (const QRect &rect, int dy); 317 void onUpdateRequesting (const QRect&, int dy); 318 void onSelectionChanged(); 319 void scrollWithInertia(); 320 321 private: 322 QString computeIndentation (const QTextCursor &cur) const; 323 QString remainingSpaces (const QString& spaceTab, const QTextCursor& cursor) const; 324 QTextCursor backTabCursor(const QTextCursor& cursor, bool twoSpace) const; 325 326 int prevAnchor_, prevPos_; // used only for bracket matching 327 QWidget *lineNumberArea_; 328 QTextEdit::ExtraSelection currentLine_; 329 QRect lastCurrentLine_; 330 int widestDigit_; 331 bool autoIndentation_; 332 bool autoReplace_; 333 bool drawIndetLines_; 334 bool autoBracket_; 335 int darkValue_; 336 QColor separatorColor_; 337 int vLineDistance_; 338 QString dateFormat_; 339 QColor lineHColor_; 340 int resizeTimerId_, selectionTimerId_; // for not wasting CPU's time 341 QPoint pressPoint_; // used internally for hyperlinks 342 QFont font_; // used internally for keeping track of the unzoomed font 343 QString textTab_; // text tab in terms of spaces 344 QElapsedTimer tripleClickTimer_; 345 /* To know whether text may be pasted, in contrast to text/file dropping: */ 346 bool pasting_; 347 /* To keep text cursor's horizontal position with Up/Down keys 348 (also used in a workaround for a Qt regression): */ 349 bool keepTxtCurHPos_; 350 int txtCurHPos_; 351 /******************************************** 352 ***** All needed information on a page ***** 353 ********************************************/ 354 qint64 size_; // file size for limiting syntax highlighting (the file may be removed) 355 QDateTime lastModified_; // the last modification time for knowing about changes. 356 int wordNumber_; // the calculated number of words (-1 if not counted yet) 357 QString searchedText_; // the text that is being searched in the document 358 QString replaceTitle_; // the title of the Replacement dock (can change) 359 QString fileName_; // opened file 360 QString prog_; // real programming language (never empty; defaults to "url") 361 QString lang_; // selected (enforced) programming language (empty if nothing's enforced) 362 QString encoding_; // text encoding (UTF-8 by default) 363 /* 364 Highlighting order: (1) current line; 365 (2) replacing; 366 (3) search matches; 367 (4) selection matches; 368 (5) bracket matches. 369 */ 370 QList<QTextEdit::ExtraSelection> greenSel_; // for replaced matches 371 QList<QTextEdit::ExtraSelection> blueSel_; // for selection highlighting 372 QList<QTextEdit::ExtraSelection> redSel_; // for bracket matching 373 bool selectionHighlighting_; // should selections be highlighted? 374 bool highlightThisSelection_; // should this selection be highlighted? 375 bool removeSelectionHighlights_; // used only internally 376 bool matchedBrackets_; // is bracket matching done (is FPwin::matchBrackets called)? 377 bool uneditable_; // the doc should be made uneditable because of its contents 378 QSyntaxHighlighter *highlighter_; // syntax highlighter 379 bool saveCursor_; 380 bool pastePaths_; 381 /****************************** 382 ***** Inertial scrolling ***** 383 ******************************/ 384 bool inertialScrolling_; 385 struct scrollData { 386 int delta; 387 int leftSteps; 388 int totalSteps; 389 }; 390 QList<scrollData> queuedScrollSteps_; 391 QTimer *scrollTimer_; 392 }; 393 /*************************/ 394 class LineNumberArea : public QWidget 395 { 396 Q_OBJECT 397 398 public: LineNumberArea(TextEdit * Editor)399 LineNumberArea (TextEdit *Editor) : QWidget (Editor) { 400 editor = Editor; 401 } 402 sizeHint()403 QSize sizeHint() const { 404 return QSize (editor->lineNumberAreaWidth(), 0); 405 } 406 407 protected: paintEvent(QPaintEvent * event)408 void paintEvent (QPaintEvent *event) { 409 editor->lineNumberAreaPaintEvent (event); 410 } 411 mouseDoubleClickEvent(QMouseEvent * event)412 void mouseDoubleClickEvent (QMouseEvent *event) { 413 if (rect().contains (event->pos())) 414 editor->centerCursor(); 415 QWidget::mouseDoubleClickEvent (event); 416 } 417 418 private: 419 TextEdit *editor; 420 }; 421 422 } 423 424 #endif // TEXTEDIT_H 425