]>
Commit | Line | Data |
---|---|---|
20476098 | 1 | #include <ctype.h> |
139f06bc | 2 | #include <slang.h> |
20476098 | 3 | #include <stdlib.h> |
05130221 | 4 | #include <string.h> |
20476098 | 5 | |
6 | #include "newt.h" | |
7 | #include "newt_pr.h" | |
8 | ||
9 | struct textbox { | |
10 | char ** lines; | |
11 | int numLines; | |
12 | int linesAlloced; | |
13 | int doWrap; | |
14 | newtComponent sb; | |
15 | int topLine; | |
8f52cd47 | 16 | int textWidth; |
20476098 | 17 | }; |
18 | ||
19 | static void addLine(newtComponent co, const char * s, int len); | |
20 | static void textboxDraw(newtComponent co); | |
21 | static void addShortLine(newtComponent co, const char * s, int len); | |
45c366b1 | 22 | static struct eventResult textboxEvent(newtComponent c, |
20476098 | 23 | struct event ev); |
24 | static void textboxDestroy(newtComponent co); | |
8f52cd47 | 25 | static void textboxPlace(newtComponent co, int newLeft, int newTop); |
26 | static void textboxMapped(newtComponent co, int isMapped); | |
20476098 | 27 | |
28 | static struct componentOps textboxOps = { | |
29 | textboxDraw, | |
30 | textboxEvent, | |
31 | textboxDestroy, | |
8f52cd47 | 32 | textboxPlace, |
33 | textboxMapped, | |
20476098 | 34 | } ; |
35 | ||
8f52cd47 | 36 | static void textboxMapped(newtComponent co, int isMapped) { |
37 | struct textbox * tb = co->data; | |
38 | ||
39 | co->isMapped = isMapped; | |
40 | if (tb->sb) | |
41 | tb->sb->ops->mapped(tb->sb, isMapped); | |
42 | } | |
43 | ||
44 | static void textboxPlace(newtComponent co, int newLeft, int newTop) { | |
45 | struct textbox * tb = co->data; | |
46 | ||
47 | co->top = newTop; | |
48 | co->left = newLeft; | |
49 | ||
50 | if (tb->sb) | |
51 | tb->sb->ops->place(tb->sb, co->left + co->width - 1, co->top); | |
52 | } | |
53 | ||
4b5d9a60 | 54 | void newtTextboxSetHeight(newtComponent co, int height) { |
55 | co->height = height; | |
56 | } | |
57 | ||
58 | int newtTextboxGetNumLines(newtComponent co) { | |
59 | struct textbox * tb = co->data; | |
60 | ||
61 | return (tb->numLines); | |
62 | } | |
63 | ||
f1d6b549 | 64 | newtComponent newtTextboxReflowed(int left, int top, char * text, int width, |
65 | int flexDown, int flexUp, int flags) { | |
66 | newtComponent co; | |
67 | char * reflowedText; | |
68 | int actWidth, actHeight; | |
69 | ||
70 | reflowedText = newtReflowText(text, width, flexDown, flexUp, | |
71 | &actWidth, &actHeight); | |
72 | ||
73 | co = newtTextbox(-1, -1, actWidth, actHeight, NEWT_TEXTBOX_WRAP); | |
74 | newtTextboxSetText(co, reflowedText); | |
75 | free(reflowedText); | |
76 | ||
77 | return co; | |
78 | } | |
79 | ||
20476098 | 80 | newtComponent newtTextbox(int left, int top, int width, int height, int flags) { |
81 | newtComponent co; | |
82 | struct textbox * tb; | |
83 | ||
84 | co = malloc(sizeof(*co)); | |
85 | tb = malloc(sizeof(*tb)); | |
86 | co->data = tb; | |
87 | ||
88 | co->ops = &textboxOps; | |
89 | ||
90 | co->height = height; | |
91 | co->top = top; | |
92 | co->left = left; | |
93 | co->takesFocus = 0; | |
8f52cd47 | 94 | co->width = width; |
20476098 | 95 | |
4a93351d | 96 | tb->doWrap = flags & NEWT_FLAG_WRAP; |
20476098 | 97 | tb->numLines = 0; |
98 | tb->linesAlloced = 0; | |
99 | tb->lines = NULL; | |
100 | tb->topLine = 0; | |
8f52cd47 | 101 | tb->textWidth = width; |
20476098 | 102 | |
4a93351d | 103 | if (flags & NEWT_FLAG_SCROLL) { |
8f52cd47 | 104 | co->width += 2; |
105 | tb->sb = newtVerticalScrollbar(co->left + co->width - 1, co->top, | |
20476098 | 106 | co->height, COLORSET_TEXTBOX, COLORSET_TEXTBOX); |
107 | } else { | |
20476098 | 108 | tb->sb = NULL; |
109 | } | |
110 | ||
111 | return co; | |
112 | } | |
113 | ||
a7582573 | 114 | static void doReflow(char * text, char ** resultPtr, int width, int * badness, |
115 | int * heightPtr) { | |
116 | char * result = NULL; | |
117 | char * chptr, * end; | |
118 | int howbad = 0; | |
119 | int height = 0; | |
120 | ||
121 | if (resultPtr) { | |
122 | /* XXX I think this will work */ | |
123 | result = malloc(strlen(text) + (strlen(text) / width) + 2); | |
124 | *result = '\0'; | |
125 | } | |
126 | ||
127 | while (*text) { | |
128 | end = strchr(text, '\n'); | |
129 | if (!end) | |
130 | end = text + strlen(text); | |
131 | ||
132 | while (*text && text <= end) { | |
133 | if (end - text < width) { | |
134 | if (result) { | |
135 | strncat(result, text, end - text); | |
136 | strcat(result, "\n"); | |
137 | height++; | |
138 | } | |
74c9edf7 | 139 | |
140 | if (end - text < (width / 2)) | |
8833c01f | 141 | howbad += ((width / 2) - (end - text)) / 2; |
a7582573 | 142 | text = end; |
143 | if (*text) text++; | |
144 | } else { | |
145 | chptr = text + width - 1; | |
146 | while (chptr > text && !isspace(*chptr)) chptr--; | |
147 | while (isspace(*chptr)) chptr--; | |
148 | chptr++; | |
149 | ||
150 | if (chptr > text) | |
151 | howbad += width - (chptr - text) + 1; | |
152 | if (result) { | |
153 | strncat(result, text, chptr - text); | |
154 | strcat(result, "\n"); | |
155 | height++; | |
156 | } | |
157 | ||
158 | text = chptr + 1; | |
159 | while (isspace(*text)) text++; | |
160 | } | |
161 | } | |
162 | } | |
163 | ||
164 | if (badness) *badness = howbad; | |
165 | if (resultPtr) *resultPtr = result; | |
166 | if (heightPtr) *heightPtr = height; | |
167 | } | |
168 | ||
169 | char * newtReflowText(char * text, int width, int flexDown, int flexUp, | |
170 | int * actualWidth, int * actualHeight) { | |
171 | int min, max; | |
172 | int i; | |
173 | char * result; | |
174 | int minbad, minbadwidth, howbad; | |
175 | ||
176 | if (flexDown || flexUp) { | |
177 | min = width - flexDown; | |
178 | max = width + flexUp; | |
179 | ||
180 | minbad = -1; | |
181 | minbadwidth = width; | |
182 | ||
183 | for (i = min; i <= max; i++) { | |
184 | doReflow(text, NULL, i, &howbad, NULL); | |
185 | ||
186 | if (minbad == -1 || howbad < minbad) { | |
187 | minbad = howbad; | |
188 | minbadwidth = i; | |
189 | } | |
190 | } | |
191 | ||
192 | width = minbadwidth; | |
193 | } | |
194 | ||
195 | doReflow(text, &result, width, NULL, actualHeight); | |
196 | if (actualWidth) *actualWidth = width; | |
197 | return result; | |
198 | } | |
199 | ||
20476098 | 200 | void newtTextboxSetText(newtComponent co, const char * text) { |
201 | const char * start, * end; | |
202 | struct textbox * tb = co->data; | |
203 | ||
204 | if (tb->lines) { | |
205 | free(tb->lines); | |
206 | tb->numLines = 0; | |
207 | } | |
208 | ||
209 | tb->linesAlloced = 10; | |
210 | tb->lines = malloc(sizeof(char *) * 10); | |
211 | ||
212 | start = text; | |
213 | while ((end = strchr(start, '\n'))) { | |
214 | addLine(co, start, end - start); | |
215 | start = end + 1; | |
216 | } | |
217 | ||
218 | if (*start) | |
219 | addLine(co, start, -1); | |
220 | } | |
221 | ||
222 | static void addLine(newtComponent co, const char * s, int origlen) { | |
223 | const char * start, * end; | |
224 | int len; | |
225 | struct textbox * tb = co->data; | |
226 | ||
227 | if (origlen < 0) origlen = strlen(s); | |
228 | len = origlen; | |
229 | ||
8f52cd47 | 230 | if (!tb->doWrap || len <= tb->textWidth) { |
20476098 | 231 | addShortLine(co, s, len); |
232 | } else { | |
233 | /* word wrap */ | |
234 | ||
235 | start = s; | |
8f52cd47 | 236 | while (len > tb->textWidth) { |
237 | end = start + tb->textWidth - 1; | |
20476098 | 238 | while (end > start && !isspace(*end)) end--; |
239 | ||
240 | if (end == start) | |
8f52cd47 | 241 | end = start + tb->textWidth - 1; |
20476098 | 242 | |
243 | addShortLine(co, start, end - start); | |
244 | ||
245 | start = end + 1; | |
246 | while (isspace(*start) && *start) start++; | |
247 | ||
248 | len = origlen - (start - s); | |
249 | } | |
250 | ||
251 | if (*start) | |
252 | addShortLine(co, start, len); | |
253 | } | |
254 | } | |
255 | ||
256 | static void addShortLine(newtComponent co, const char * s, int len) { | |
257 | struct textbox * tb = co->data; | |
258 | ||
259 | if (tb->linesAlloced == tb->numLines) { | |
260 | tb->linesAlloced += 10; | |
261 | tb->lines = realloc(tb->lines, (sizeof(char *) * tb->linesAlloced)); | |
262 | } | |
263 | ||
8f52cd47 | 264 | if (len > tb->textWidth) len = tb->textWidth; |
20476098 | 265 | |
8f52cd47 | 266 | tb->lines[tb->numLines] = malloc(tb->textWidth + 1); |
20476098 | 267 | strncpy(tb->lines[tb->numLines], s, len); |
268 | ||
8f52cd47 | 269 | while (len < tb->textWidth) tb->lines[tb->numLines][len++] = ' '; |
20476098 | 270 | tb->lines[tb->numLines++][len] = '\0'; |
271 | } | |
272 | ||
273 | static void textboxDraw(newtComponent c) { | |
274 | int i; | |
275 | struct textbox * tb = c->data; | |
276 | int size; | |
277 | ||
278 | if (tb->sb) { | |
279 | size = tb->numLines - c->height; | |
280 | newtScrollbarSet(tb->sb, tb->topLine, size ? size : 0); | |
281 | tb->sb->ops->draw(tb->sb); | |
282 | } | |
676cfb93 | 283 | |
284 | SLsmg_set_color(NEWT_COLORSET_TEXTBOX); | |
20476098 | 285 | |
286 | for (i = 0; (i + tb->topLine) < tb->numLines && i < c->height; i++) { | |
287 | newtGotorc(c->top + i, c->left); | |
288 | SLsmg_write_string(tb->lines[i + tb->topLine]); | |
289 | } | |
290 | } | |
291 | ||
45c366b1 | 292 | static struct eventResult textboxEvent(newtComponent co, |
20476098 | 293 | struct event ev) { |
294 | struct textbox * tb = co->data; | |
295 | struct eventResult er; | |
296 | ||
297 | er.result = ER_IGNORED; | |
298 | ||
299 | if (ev.when == EV_EARLY && ev.event == EV_KEYPRESS && tb->sb) { | |
300 | switch (ev.u.key) { | |
301 | case NEWT_KEY_UP: | |
302 | if (tb->topLine) tb->topLine--; | |
303 | textboxDraw(co); | |
304 | er.result = ER_SWALLOWED; | |
305 | break; | |
306 | ||
307 | case NEWT_KEY_DOWN: | |
308 | if (tb->topLine < (tb->numLines - co->height)) tb->topLine++; | |
309 | textboxDraw(co); | |
310 | er.result = ER_SWALLOWED; | |
311 | break; | |
312 | } | |
313 | } | |
314 | ||
315 | return er; | |
316 | } | |
317 | ||
318 | static void textboxDestroy(newtComponent co) { | |
319 | int i; | |
320 | struct textbox * tb = co->data; | |
321 | ||
322 | for (i = 0; i < tb->numLines; i++) | |
323 | free(tb->lines[i]); | |
324 | free(tb->lines); | |
325 | free(tb); | |
326 | free(co); | |
327 | } |