]> git.ipfire.org Git - thirdparty/newt.git/blame - textbox.c
version 0.23
[thirdparty/newt.git] / textbox.c
CommitLineData
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
9struct 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
19static void addLine(newtComponent co, const char * s, int len);
20static void textboxDraw(newtComponent co);
21static void addShortLine(newtComponent co, const char * s, int len);
45c366b1 22static struct eventResult textboxEvent(newtComponent c,
20476098 23 struct event ev);
24static void textboxDestroy(newtComponent co);
8f52cd47 25static void textboxPlace(newtComponent co, int newLeft, int newTop);
26static void textboxMapped(newtComponent co, int isMapped);
20476098 27
28static struct componentOps textboxOps = {
29 textboxDraw,
30 textboxEvent,
31 textboxDestroy,
8f52cd47 32 textboxPlace,
33 textboxMapped,
20476098 34} ;
35
8f52cd47 36static 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
44static 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 54void newtTextboxSetHeight(newtComponent co, int height) {
55 co->height = height;
56}
57
58int newtTextboxGetNumLines(newtComponent co) {
59 struct textbox * tb = co->data;
60
61 return (tb->numLines);
62}
63
f1d6b549 64newtComponent 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 80newtComponent 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 114static 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
169char * 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 200void 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
222static 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
256static 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
273static 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 292static 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
318static 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}