| 1 | // This file is part of SmallBASIC |
| 2 | // |
| 3 | // Copyright(C) 2001-2020 Chris Warren-Smith. |
| 4 | // |
| 5 | // This program is distributed under the terms of the GPL v2.0 or later |
| 6 | // Download the GNU Public License (GPL) from www.gnu.org |
| 7 | // |
| 8 | |
| 9 | #ifndef TEXTEDIT_H |
| 10 | #define TEXTEDIT_H |
| 11 | |
| 12 | #define STB_TEXTEDIT_CHARTYPE char |
| 13 | #define STB_TEXTEDIT_UNDOCHARCOUNT 5000 |
| 14 | #define MARGIN_CHARS 4 |
| 15 | #define MAX_MARKERS 10 |
| 16 | |
| 17 | #include <config.h> |
| 18 | #include <stdlib.h> |
| 19 | #include <string.h> |
| 20 | #include <ctype.h> |
| 21 | #include "lib/stb/stb_textedit.h" |
| 22 | #include "ui/inputs.h" |
| 23 | #include "ui/theme.h" |
| 24 | #include "common/smbas.h" |
| 25 | #include "common/keymap.h" |
| 26 | |
| 27 | struct TextEditInput; |
| 28 | |
| 29 | struct StackTraceNode { |
| 30 | StackTraceNode(const char *keyword, int type, int line) |
| 31 | : _keyword(keyword), _type(type), _line(line) {} |
| 32 | virtual ~StackTraceNode() {} |
| 33 | const char *_keyword; |
| 34 | int _type; |
| 35 | int _line; |
| 36 | }; |
| 37 | |
| 38 | typedef strlib::List<StackTraceNode *> StackTrace; |
| 39 | |
| 40 | struct EditBuffer { |
| 41 | char *_buffer; |
| 42 | int _len; |
| 43 | int _size; |
| 44 | int _lines; |
| 45 | TextEditInput *_in; |
| 46 | |
| 47 | EditBuffer(TextEditInput *in, const char *text); |
| 48 | virtual ~EditBuffer(); |
| 49 | |
| 50 | void append(const char *text, int len) { insertChars(_len, text, len); } |
| 51 | void append(const char *text) { insertChars(_len, text, strlen(text)); } |
| 52 | void clear(); |
| 53 | int countNewlines(const char *text, int num); |
| 54 | int deleteChars(int pos, int num); |
| 55 | char getChar(int pos); |
| 56 | int insertChars(int pos, const char *text, int num); |
| 57 | int lineCount(); |
| 58 | void removeTrailingSpaces(STB_TexteditState *state); |
| 59 | char *(int start, int end); |
| 60 | }; |
| 61 | |
| 62 | struct TextEditInput : public FormEditInput { |
| 63 | TextEditInput(const char *text, int chW, int chH, int x, int y, int w, int h); |
| 64 | virtual ~TextEditInput(); |
| 65 | |
| 66 | void append(const char *text, int len) { _buf.append(text, len); } |
| 67 | void completeWord(const char *word); |
| 68 | const char *completeKeyword(int index); |
| 69 | void draw(int x, int y, int w, int h, int chw); |
| 70 | bool edit(int key, int screenWidth, int charWidth); |
| 71 | bool find(const char *word, bool next); |
| 72 | int getCursorPos() const { return _state.cursor; } |
| 73 | int getCol() const { return _cursorCol; } |
| 74 | int getRow() const { return _cursorRow + 1; } |
| 75 | int () const { return _height / _charHeight; } |
| 76 | int getLines() { return _buf.lineCount(); } |
| 77 | int getMarginWidth() { return _marginWidth; } |
| 78 | void getSelectionCounts(int *lines, int *chars); |
| 79 | int getSelectionRow(); |
| 80 | int getSelectionStart() { return _state.select_start; } |
| 81 | int getScroll() const { return _scroll; } |
| 82 | const char *getText() const { return _buf._buffer; } |
| 83 | char *getTextSelection(bool selectAll); |
| 84 | int getTextLength() const { return _buf._len; } |
| 85 | int *getMarkers(); |
| 86 | void gotoLine(const char *buffer); |
| 87 | void reload(const char *text); |
| 88 | bool save(const char *filePath); |
| 89 | void setCursor(int pos); |
| 90 | void setCursorPos(int pos); |
| 91 | void setCursorRow(int row); |
| 92 | void setLineNumbers() { _marginWidth = 1 + (_charWidth * MARGIN_CHARS); } |
| 93 | void setText(const char *text) { _buf.clear(); _buf.append(text); } |
| 94 | void setTheme(EditTheme *theme) { _theme = theme; } |
| 95 | void clicked(int x, int y, bool pressed); |
| 96 | void updateField(var_p_t form); |
| 97 | bool updateUI(var_p_t form, var_p_t field); |
| 98 | bool selected(MAPoint2d pt, int scrollX, int scrollY, bool &redraw); |
| 99 | int padding(bool) const { return 0; } |
| 100 | void layout(StbTexteditRow *row, int start_i) const; |
| 101 | int charWidth(int k, int i) const; |
| 102 | char *copy(bool cut); |
| 103 | void paste(const char *text); |
| 104 | void selectAll(); |
| 105 | bool isDirty() { return _dirty && _state.undostate.undo_point > 0; } |
| 106 | void setDirty(bool dirty) { _dirty = dirty; } |
| 107 | void layout(int w, int h); |
| 108 | const char *getNodeId(); |
| 109 | char *getWordBeforeCursor(); |
| 110 | bool replaceNext(const char *text, bool skip); |
| 111 | int getCompletions(StringList *list, int max); |
| 112 | void selectNavigate(bool up); |
| 113 | EditTheme *getTheme() { return _theme; } |
| 114 | |
| 115 | protected: |
| 116 | enum SyntaxState { |
| 117 | kReset = 0, |
| 118 | , |
| 119 | kText, |
| 120 | kCommand, |
| 121 | kStatement, |
| 122 | kDigit, |
| 123 | }; |
| 124 | |
| 125 | void dragPage(int y, bool &redraw); |
| 126 | void drawText(int x, int y, const char *str, int length, SyntaxState &state); |
| 127 | void calcMargin(); |
| 128 | void changeCase(); |
| 129 | void cycleTheme(); |
| 130 | void drawLineNumber(int x, int y, int row, bool selected); |
| 131 | void editDeleteLine(); |
| 132 | void editEnter(); |
| 133 | void editTab(); |
| 134 | bool endStatement(const char *buf); |
| 135 | void findMatchingBrace(); |
| 136 | int getCursorRow(); |
| 137 | uint32_t getHash(const char *str, int offs, int &count); |
| 138 | int getIndent(char *spaces, int len, int pos); |
| 139 | int getLineChars(StbTexteditRow *row, int pos); |
| 140 | char *getSelection(int *start, int *end); |
| 141 | void gotoNextMarker(); |
| 142 | void killWord(); |
| 143 | void lineNavigate(bool lineDown); |
| 144 | char *lineText(int pos); |
| 145 | int lineEnd(int pos) { return linePos(pos, true); } |
| 146 | int linePos(int pos, bool end, bool excludeBreak=true); |
| 147 | int lineStart(int pos) { return linePos(pos, false); } |
| 148 | bool matchCommand(uint32_t hash); |
| 149 | bool matchStatement(uint32_t hash); |
| 150 | void pageNavigate(bool pageDown, bool shift); |
| 151 | void removeTrailingSpaces(); |
| 152 | void selectWord(); |
| 153 | void setColor(SyntaxState &state); |
| 154 | void toggleMarker(); |
| 155 | void updateScroll(); |
| 156 | int wordEnd(); |
| 157 | int wordStart(); |
| 158 | |
| 159 | EditBuffer _buf; |
| 160 | STB_TexteditState _state; |
| 161 | EditTheme *_theme; |
| 162 | int _charWidth; |
| 163 | int _charHeight; |
| 164 | int _marginWidth; |
| 165 | int _scroll; |
| 166 | int _cursorCol; |
| 167 | int _cursorRow; |
| 168 | int _cursorLine; |
| 169 | int _indentLevel; |
| 170 | int _matchingBrace; |
| 171 | int _ptY; |
| 172 | int _pressTick; |
| 173 | int _xmargin; |
| 174 | int _ymargin; |
| 175 | bool _bottom; |
| 176 | bool _dirty; |
| 177 | }; |
| 178 | |
| 179 | struct TextEditHelpWidget : public TextEditInput { |
| 180 | TextEditHelpWidget(TextEditInput *editor, int chW, int chH, bool overlay=true); |
| 181 | virtual ~TextEditHelpWidget(); |
| 182 | |
| 183 | enum HelpMode { |
| 184 | kNone, |
| 185 | kHelp, |
| 186 | kHelpKeyword, |
| 187 | kCompletion, |
| 188 | kOutline, |
| 189 | kSearch, |
| 190 | kEnterReplace, |
| 191 | kEnterReplaceWith, |
| 192 | kReplace, |
| 193 | kReplaceDone, |
| 194 | kGotoLine, |
| 195 | kMessage, |
| 196 | kLineEdit, |
| 197 | kStacktrace |
| 198 | }; |
| 199 | |
| 200 | void clicked(int x, int y, bool pressed); |
| 201 | void createCompletionHelp(); |
| 202 | void createGotoLine(); |
| 203 | void createHelp(); |
| 204 | void createLineEdit(const char *value); |
| 205 | void createKeywordIndex(); |
| 206 | void createMessage() { reset(kMessage); } |
| 207 | void createOutline(); |
| 208 | void createSearch(bool replace); |
| 209 | void createStackTrace(const char *error, int line, StackTrace &trace); |
| 210 | void draw(int x, int y, int w, int h, int chw); |
| 211 | bool edit(int key, int screenWidth, int charWidth); |
| 212 | void paste(const char *text); |
| 213 | bool isDrawTop() { return true; } |
| 214 | void reset(HelpMode mode); |
| 215 | void cancelMode() { _mode = kNone; } |
| 216 | bool closeOnEnter() const; |
| 217 | bool searchMode() const { return _mode >= kSearch && _mode <= kReplaceDone; } |
| 218 | void layout(int w, int h); |
| 219 | bool lineEditMode() const { return _mode == kLineEdit; } |
| 220 | bool messageMode() const { return _mode == kMessage; } |
| 221 | bool replaceMode() const { return _mode == kReplace; } |
| 222 | bool replaceModeWith() const { return _mode == kEnterReplaceWith; } |
| 223 | bool replaceDoneMode() const { return _mode == kReplaceDone; } |
| 224 | bool selected(MAPoint2d pt, int scrollX, int scrollY, bool &redraw); |
| 225 | void (int cols, int rows); |
| 226 | void (); |
| 227 | void toggleKeyword(); |
| 228 | |
| 229 | private: |
| 230 | void completeLine(int pos); |
| 231 | void completeWord(int pos); |
| 232 | void createPackageIndex(); |
| 233 | |
| 234 | HelpMode _mode; |
| 235 | strlib::List<int *> _outline; |
| 236 | TextEditInput *_editor; |
| 237 | const char *_openPackage; |
| 238 | int _openKeyword; |
| 239 | enum Layout { |
| 240 | kLine, |
| 241 | , |
| 242 | , |
| 243 | } _layout; |
| 244 | }; |
| 245 | |
| 246 | #define STB_TEXTEDIT_STRING EditBuffer |
| 247 | #define STB_TEXTEDIT_K_LEFT SB_KEY_LEFT |
| 248 | #define STB_TEXTEDIT_K_RIGHT SB_KEY_RIGHT |
| 249 | #define STB_TEXTEDIT_K_UP SB_KEY_UP |
| 250 | #define STB_TEXTEDIT_K_DOWN SB_KEY_DOWN |
| 251 | #define STB_TEXTEDIT_K_LINESTART SB_KEY_HOME |
| 252 | #define STB_TEXTEDIT_K_LINEEND SB_KEY_END |
| 253 | #define STB_TEXTEDIT_K_DELETE SB_KEY_DELETE |
| 254 | #define STB_TEXTEDIT_K_BACKSPACE SB_KEY_BACKSPACE |
| 255 | #define STB_TEXTEDIT_K_TEXTSTART SB_KEY_CTRL(SB_KEY_HOME) |
| 256 | #define STB_TEXTEDIT_K_TEXTEND SB_KEY_CTRL(SB_KEY_END) |
| 257 | #define STB_TEXTEDIT_K_UNDO SB_KEY_CTRL('z') |
| 258 | #define STB_TEXTEDIT_K_REDO SB_KEY_CTRL('y') |
| 259 | #define STB_TEXTEDIT_K_INSERT SB_KEY_INSERT |
| 260 | #define STB_TEXTEDIT_K_WORDLEFT SB_KEY_CTRL(SB_KEY_LEFT) |
| 261 | #define STB_TEXTEDIT_K_WORDRIGHT SB_KEY_CTRL(SB_KEY_RIGHT) |
| 262 | #define STB_TEXTEDIT_K_PGUP SB_KEY_PGUP |
| 263 | #define STB_TEXTEDIT_K_PGDOWN SB_KEY_PGDN |
| 264 | #define STB_TEXTEDIT_NEWLINE '\n' |
| 265 | #define STB_TEXTEDIT_K_CONTROL SB_KEY_CTRL(0) |
| 266 | #define STB_TEXTEDIT_K_SHIFT SB_KEY_SHIFT(0) |
| 267 | #define STB_TEXTEDIT_GETWIDTH_NEWLINE 0.0f |
| 268 | #define STB_TEXTEDIT_KEYTOTEXT(k) k |
| 269 | #define STB_TEXTEDIT_STRINGLEN(o) o->_len |
| 270 | #define STB_TEXTEDIT_GETCHAR(o,i) o->getChar(i) |
| 271 | #define STB_TEXTEDIT_GETWIDTH(o,n,i) o->_in->charWidth(n, i) |
| 272 | #define STB_TEXTEDIT_LAYOUTROW(r,o,n) o->_in->layout(r, n) |
| 273 | #define STB_TEXTEDIT_DELETECHARS(o,i,n) o->deleteChars(i, n) |
| 274 | #define STB_TEXTEDIT_INSERTCHARS(o,i,c,n) o->insertChars(i, c, n) |
| 275 | |
| 276 | #endif |
| 277 | |
| 278 | |