1// This file is part of SmallBASIC
2//
3// Copyright(C) 2001-2019 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 Fl_TTY_WIDGET
10#define Fl_TTY_WIDGET
11
12#include <stdlib.h>
13#include <string.h>
14#include <FL/fl_draw.H>
15#include <FL/Fl_Widget.H>
16#include <FL/Fl_Group.H>
17#include <FL/Fl_Scrollbar.H>
18#include "ui/strlib.h"
19#include "platform/fltk/utils.h"
20
21using namespace strlib;
22
23struct Point {
24 int x;
25 int y;
26};
27
28struct TtyTextSeg {
29 enum {
30 BOLD = 0x00000001,
31 ITALIC = 0x00000002,
32 UNDERLINE = 0x00000004,
33 INVERT = 0x00000008,
34 };
35
36 // create a new segment
37 TtyTextSeg() {
38 this->str = 0;
39 this->flags = 0;
40 this->color = 0;
41 this->next = 0;
42 }
43
44 ~TtyTextSeg() {
45 if (str) {
46 delete[]str;
47 }
48 }
49
50 // reset all flags
51 void reset() {
52 set(BOLD, false);
53 set(ITALIC, false);
54 set(UNDERLINE, false);
55 set(INVERT, false);
56 }
57
58 void setText(const char *str, int n) {
59 if ((!str || !n)) {
60 this->str = 0;
61 } else {
62 this->str = new char[n + 1];
63 strncpy(this->str, str, n);
64 this->str[n] = 0;
65 }
66 }
67
68 // create a string of n spaces
69 void tab(int n) {
70 this->str = new char[n + 1];
71 memset(this->str, ' ', n);
72 this->str[n] = 0;
73 }
74
75 // set the flag value
76 void set(int f, bool value) {
77 if (value) {
78 flags |= f;
79 } else {
80 flags &= ~f;
81 }
82 flags |= (f << 16);
83 }
84
85 // return whether the flag was set (to true or false)
86 bool set(int f) {
87 return (flags & (f << 16));
88 }
89
90 // return the flag value if set, otherwise return value
91 bool get(int f, bool *value) {
92 bool result = *value;
93 if (flags & (f << 16)) {
94 result = (flags & f);
95 }
96 return result;
97 }
98
99 // width of this segment in pixels
100 int width() {
101 return !str ? 0 : fl_width(str);
102 }
103
104 // number of chars in this segment
105 int numChars() {
106 return !str ? 0 : strlen(str);
107 }
108
109 // update font and state variables when set in this segment
110 bool escape(bool *bold, bool *italic, bool *underline, bool *invert) {
111 *bold = get(BOLD, bold);
112 *italic = get(ITALIC, italic);
113 *underline = get(UNDERLINE, underline);
114 *invert = get(INVERT, invert);
115
116 if (this->color) {
117 fl_color(this->color);
118 }
119
120 return set(BOLD) || set(ITALIC);
121 }
122
123 char *str;
124 int flags;
125 Fl_Color color;
126 TtyTextSeg *next;
127};
128
129struct TtyRow {
130 TtyRow() : head(0) {
131 }
132 ~TtyRow() {
133 clear();
134 }
135
136 // append a segment to this row
137 void append(TtyTextSeg *node) {
138 if (!head) {
139 head = node;
140 } else {
141 tail(head)->next = node;
142 }
143 node->next = 0;
144 }
145
146 // clear the contents of this row
147 void clear() {
148 remove(head);
149 head = 0;
150 }
151
152 // number of characters in this row
153 int numChars() {
154 return numChars(this->head);
155 }
156
157 int numChars(TtyTextSeg *next) {
158 int n = 0;
159 if (next) {
160 n = next->numChars() + numChars(next->next);
161 }
162 return n;
163 }
164
165 void remove(TtyTextSeg *next) {
166 if (next) {
167 remove(next->next);
168 delete next;
169 }
170 }
171
172 // move to the tab position
173 void tab() {
174 int tabSize = 6;
175 int num = numChars(this->head);
176 int pos = tabSize - (num % tabSize);
177 if (pos) {
178 TtyTextSeg *next = new TtyTextSeg();
179 next->tab(pos);
180 append(next);
181 }
182 }
183
184 TtyTextSeg *tail(TtyTextSeg *next) {
185 return !next->next ? next : tail(next->next);
186 }
187
188 int width() {
189 return width(this->head);
190 }
191
192 int width(TtyTextSeg *next) {
193 int n = 0;
194 if (next) {
195 n = next->width() + width(next->next);
196 }
197 return n;
198 }
199
200 TtyTextSeg *head;
201};
202
203struct TtyWidget : public Fl_Group {
204 TtyWidget(int x, int y, int w, int h, int numRows);
205 virtual ~TtyWidget();
206
207 // inherited methods
208 void draw();
209 int handle(int e);
210 void resize(int x, int y, int w, int h);
211
212 // public api
213 void clearScreen();
214 bool copySelection();
215 void print(const char *str);
216 void setFont(Fl_Font font) {
217 setfont(font, 0);
218 redraw();
219 };
220 void setFontSize(int size) {
221 setfont(0, size);
222 redraw();
223 };
224 void setScrollLock(bool b) {
225 scrollLock = b;
226 };
227
228private:
229 void drawSelection(TtyTextSeg *seg, strlib::String *s, int row, int x, int y);
230 TtyRow *getLine(int ndx);
231 int processLine(TtyRow *line, const char *linePtr);
232 void setfont(bool bold, bool italic);
233 void setfont(Fl_Font font, int size);
234 void setGraphicsRendition(TtyTextSeg *segment, int c);
235
236 // returns the number of display text rows held in the buffer
237 int getTextRows() {
238 return 1 + ((head >= tail) ? (head - tail) : head + (rows - tail));
239 }
240
241 // returns the number of rows available for display
242 int getPageRows() {
243 return (h() - 1) / lineHeight;
244 }
245
246 // returns the selected row within the circular buffer
247 int rowEvent() {
248 return ((Fl::event_y() - y()) / lineHeight) + tail + vscrollbar->value();
249 }
250
251 // buffer management
252 TtyRow *buffer;
253 int head; // current head of buffer
254 int tail; // buffer last line
255 int rows; // total number of rows - size of buffer
256 int cols; // maximum number of characters in a row
257 int width; // the maximum width of the buffer text in pixels
258
259 // scrollbars
260 Fl_Scrollbar *vscrollbar;
261 Fl_Scrollbar *hscrollbar;
262 int lineHeight;
263 bool scrollLock;
264
265 // clipboard handling
266 int markX, markY, pointX, pointY;
267};
268
269#endif
270