]> git.ipfire.org Git - thirdparty/newt.git/blame - textbox.c
fix URL (#68980)
[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
aa2a62d9 19static char * expandTabs(const char * text);
20476098 20static void textboxDraw(newtComponent co);
aa2a62d9 21static void addLine(newtComponent co, const char * s, int len);
22static void doReflow(const char * text, char ** resultPtr, int width,
23 int * badness, int * heightPtr);
45c366b1 24static struct eventResult textboxEvent(newtComponent c,
20476098 25 struct event ev);
26static void textboxDestroy(newtComponent co);
8f52cd47 27static void textboxPlace(newtComponent co, int newLeft, int newTop);
28static void textboxMapped(newtComponent co, int isMapped);
20476098 29
30static struct componentOps textboxOps = {
31 textboxDraw,
32 textboxEvent,
33 textboxDestroy,
8f52cd47 34 textboxPlace,
35 textboxMapped,
20476098 36} ;
37
8f52cd47 38static void textboxMapped(newtComponent co, int isMapped) {
39 struct textbox * tb = co->data;
40
41 co->isMapped = isMapped;
42 if (tb->sb)
43 tb->sb->ops->mapped(tb->sb, isMapped);
44}
45
46static void textboxPlace(newtComponent co, int newLeft, int newTop) {
47 struct textbox * tb = co->data;
48
49 co->top = newTop;
50 co->left = newLeft;
51
52 if (tb->sb)
53 tb->sb->ops->place(tb->sb, co->left + co->width - 1, co->top);
54}
55
4b5d9a60 56void newtTextboxSetHeight(newtComponent co, int height) {
57 co->height = height;
58}
59
60int newtTextboxGetNumLines(newtComponent co) {
61 struct textbox * tb = co->data;
62
63 return (tb->numLines);
64}
65
f1d6b549 66newtComponent newtTextboxReflowed(int left, int top, char * text, int width,
67 int flexDown, int flexUp, int flags) {
68 newtComponent co;
69 char * reflowedText;
70 int actWidth, actHeight;
71
72 reflowedText = newtReflowText(text, width, flexDown, flexUp,
73 &actWidth, &actHeight);
74
5fab4758 75 co = newtTextbox(left, top, actWidth, actHeight, NEWT_FLAG_WRAP);
f1d6b549 76 newtTextboxSetText(co, reflowedText);
77 free(reflowedText);
78
79 return co;
80}
81
20476098 82newtComponent newtTextbox(int left, int top, int width, int height, int flags) {
83 newtComponent co;
84 struct textbox * tb;
85
86 co = malloc(sizeof(*co));
87 tb = malloc(sizeof(*tb));
88 co->data = tb;
89
90 co->ops = &textboxOps;
91
92 co->height = height;
93 co->top = top;
94 co->left = left;
95 co->takesFocus = 0;
8f52cd47 96 co->width = width;
20476098 97
4a93351d 98 tb->doWrap = flags & NEWT_FLAG_WRAP;
20476098 99 tb->numLines = 0;
100 tb->linesAlloced = 0;
101 tb->lines = NULL;
102 tb->topLine = 0;
8f52cd47 103 tb->textWidth = width;
20476098 104
4a93351d 105 if (flags & NEWT_FLAG_SCROLL) {
8f52cd47 106 co->width += 2;
107 tb->sb = newtVerticalScrollbar(co->left + co->width - 1, co->top,
20476098 108 co->height, COLORSET_TEXTBOX, COLORSET_TEXTBOX);
109 } else {
20476098 110 tb->sb = NULL;
111 }
112
113 return co;
114}
115
aa2a62d9 116static char * expandTabs(const char * text) {
117 int bufAlloced = strlen(text) + 40;
118 char * buf, * dest;
119 const char * src;
120 int bufUsed = 0;
48f7f39d 121 int linePos = 0;
aa2a62d9 122 int i;
123
124 buf = malloc(bufAlloced + 1);
125 for (src = text, dest = buf; *src; src++) {
126 if ((bufUsed + 10) > bufAlloced) {
127 bufAlloced += strlen(text) / 2;
128 buf = realloc(buf, bufAlloced + 1);
129 dest = buf + bufUsed;
130 }
131 if (*src == '\t') {
48f7f39d 132 i = 8 - (linePos & 8);
aa2a62d9 133 memset(dest, ' ', i);
48f7f39d 134 dest += i, bufUsed += i, linePos += i;
aa2a62d9 135 } else {
48f7f39d 136 if (*src == '\n')
137 linePos = 0;
138 else
139 linePos++;
140
aa2a62d9 141 *dest++ = *src;
142 bufUsed++;
143 }
144 }
145
146 *dest = '\0';
147 return buf;
148}
149
771c47c0 150#define iseuckanji(c) (0xa1 <= (unsigned char)(c&0xff) && (unsigned char)(c&0xff) <= 0xfe)
151
aa2a62d9 152static void doReflow(const char * text, char ** resultPtr, int width,
153 int * badness, int * heightPtr) {
a7582573 154 char * result = NULL;
aa2a62d9 155 const char * chptr, * end;
771c47c0 156 int i;
a7582573 157 int howbad = 0;
158 int height = 0;
771c47c0 159 int kanji = 0;
a7582573 160
161 if (resultPtr) {
ba6b89db 162 /* XXX I think this will work */
163 result = malloc(strlen(text) + (strlen(text) / width) + 2);
63231242 164 *result = '\0';
a7582573 165 }
166
167 while (*text) {
771c47c0 168 kanji = 0;
63231242 169 end = strchr(text, '\n');
170 if (!end)
171 end = text + strlen(text);
172
173 while (*text && text <= end) {
174 if (end - text < width) {
175 if (result) {
176 strncat(result, text, end - text);
177 strcat(result, "\n");
178 height++;
a7582573 179 }
63231242 180
181 if (end - text < (width / 2))
182 howbad += ((width / 2) - (end - text)) / 2;
183 text = end;
184 if (*text) text++;
185 } else {
186 chptr = text;
187 kanji = 0;
188 for ( i = 0; i < width - 1; i++ ) {
189 if ( !iseuckanji(*chptr)) {
190 kanji = 0;
191 } else if ( kanji == 1 ) {
192 kanji = 2;
193 } else {
194 kanji = 1;
195 }
196 chptr++;
197 }
198 if (kanji == 0) {
199 while (chptr > text && !isspace(*chptr)) chptr--;
200 while (chptr > text && isspace(*chptr)) chptr--;
201 chptr++;
202 }
203
204 if (chptr-text == 1 && !isspace(*chptr))
205 chptr = text + width - 1;
206
207 if (chptr > text)
208 howbad += width - (chptr - text) + 1;
209 if (result) {
210 if (kanji == 1) {
211 strncat(result, text, chptr - text + 1 );
212 chptr++;
213 kanji = 0;
214 } else {
215 strncat(result, text, chptr - text );
216 }
217 strcat(result, "\n");
218 height++;
219 }
220
221 if (isspace(*chptr))
222 text = chptr + 1;
223 else
224 text = chptr;
225 while (isspace(*text)) text++;
226 }
a7582573 227 }
63231242 228 }
229
a7582573 230 if (badness) *badness = howbad;
231 if (resultPtr) *resultPtr = result;
232 if (heightPtr) *heightPtr = height;
233}
234
235char * newtReflowText(char * text, int width, int flexDown, int flexUp,
236 int * actualWidth, int * actualHeight) {
237 int min, max;
238 int i;
239 char * result;
240 int minbad, minbadwidth, howbad;
aa2a62d9 241 char * expandedText;
242
243 expandedText = expandTabs(text);
a7582573 244
245 if (flexDown || flexUp) {
246 min = width - flexDown;
247 max = width + flexUp;
248
249 minbad = -1;
250 minbadwidth = width;
251
252 for (i = min; i <= max; i++) {
aa2a62d9 253 doReflow(expandedText, NULL, i, &howbad, NULL);
a7582573 254
255 if (minbad == -1 || howbad < minbad) {
256 minbad = howbad;
257 minbadwidth = i;
258 }
259 }
260
261 width = minbadwidth;
262 }
263
aa2a62d9 264 doReflow(expandedText, &result, width, NULL, actualHeight);
265 free(expandedText);
a7582573 266 if (actualWidth) *actualWidth = width;
267 return result;
268}
269
20476098 270void newtTextboxSetText(newtComponent co, const char * text) {
271 const char * start, * end;
272 struct textbox * tb = co->data;
aa2a62d9 273 char * reflowed, * expanded;
274 int badness, height;
20476098 275
276 if (tb->lines) {
277 free(tb->lines);
aa2a62d9 278 tb->linesAlloced = tb->numLines = 0;
20476098 279 }
280
aa2a62d9 281 expanded = expandTabs(text);
282
283 if (tb->doWrap) {
284 doReflow(expanded, &reflowed, tb->textWidth, &badness, &height);
285 free(expanded);
286 expanded = reflowed;
287 }
288
289 for (start = expanded; *start; start++)
290 if (*start == '\n') tb->linesAlloced++;
291
292 /* This ++ leaves room for an ending line w/o a \n */
293 tb->linesAlloced++;
294 tb->lines = malloc(sizeof(char *) * tb->linesAlloced);
20476098 295
aa2a62d9 296 start = expanded;
20476098 297 while ((end = strchr(start, '\n'))) {
298 addLine(co, start, end - start);
299 start = end + 1;
300 }
301
302 if (*start)
aa2a62d9 303 addLine(co, start, strlen(start));
20476098 304
aa2a62d9 305 free(expanded);
add984c1 306
307 newtTrashScreen();
20476098 308}
309
aa2a62d9 310/* This assumes the buffer is allocated properly! */
311static void addLine(newtComponent co, const char * s, int len) {
20476098 312 struct textbox * tb = co->data;
313
8f52cd47 314 if (len > tb->textWidth) len = tb->textWidth;
20476098 315
8f52cd47 316 tb->lines[tb->numLines] = malloc(tb->textWidth + 1);
aa2a62d9 317 memset(tb->lines[tb->numLines], ' ', tb->textWidth);
318 memcpy(tb->lines[tb->numLines], s, len);
319 tb->lines[tb->numLines++][tb->textWidth] = '\0';
20476098 320}
321
322static void textboxDraw(newtComponent c) {
323 int i;
324 struct textbox * tb = c->data;
325 int size;
326
327 if (tb->sb) {
328 size = tb->numLines - c->height;
329 newtScrollbarSet(tb->sb, tb->topLine, size ? size : 0);
330 tb->sb->ops->draw(tb->sb);
331 }
676cfb93 332
333 SLsmg_set_color(NEWT_COLORSET_TEXTBOX);
7ff5fd71 334
20476098 335 for (i = 0; (i + tb->topLine) < tb->numLines && i < c->height; i++) {
336 newtGotorc(c->top + i, c->left);
337 SLsmg_write_string(tb->lines[i + tb->topLine]);
338 }
339}
340
45c366b1 341static struct eventResult textboxEvent(newtComponent co,
20476098 342 struct event ev) {
343 struct textbox * tb = co->data;
344 struct eventResult er;
345
346 er.result = ER_IGNORED;
347
348 if (ev.when == EV_EARLY && ev.event == EV_KEYPRESS && tb->sb) {
add984c1 349 newtTrashScreen();
20476098 350 switch (ev.u.key) {
351 case NEWT_KEY_UP:
352 if (tb->topLine) tb->topLine--;
353 textboxDraw(co);
354 er.result = ER_SWALLOWED;
355 break;
356
357 case NEWT_KEY_DOWN:
358 if (tb->topLine < (tb->numLines - co->height)) tb->topLine++;
359 textboxDraw(co);
360 er.result = ER_SWALLOWED;
361 break;
aa2a62d9 362
363 case NEWT_KEY_PGDN:
364 tb->topLine += co->height;
d3c09361 365 if (tb->topLine > (tb->numLines - co->height)) {
aa2a62d9 366 tb->topLine = tb->numLines - co->height;
d3c09361 367 if (tb->topLine < 0) tb->topLine = 0;
368 }
aa2a62d9 369 textboxDraw(co);
370 er.result = ER_SWALLOWED;
371 break;
372
373 case NEWT_KEY_PGUP:
374 tb->topLine -= co->height;
375 if (tb->topLine < 0) tb->topLine = 0;
376 textboxDraw(co);
377 er.result = ER_SWALLOWED;
378 break;
20476098 379 }
380 }
45f6c4fd 381 if (ev.when == EV_EARLY && ev.event == EV_MOUSE && tb->sb) {
382 /* Top scroll arrow */
383 if (ev.u.mouse.x == co->width && ev.u.mouse.y == co->top) {
384 if (tb->topLine) tb->topLine--;
385 textboxDraw(co);
386
387 er.result = ER_SWALLOWED;
388 }
389 /* Bottom scroll arrow */
390 if (ev.u.mouse.x == co->width &&
391 ev.u.mouse.y == co->top + co->height - 1) {
392 if (tb->topLine < (tb->numLines - co->height)) tb->topLine++;
393 textboxDraw(co);
394
395 er.result = ER_SWALLOWED;
396 }
397 }
20476098 398 return er;
399}
400
401static void textboxDestroy(newtComponent co) {
402 int i;
403 struct textbox * tb = co->data;
404
405 for (i = 0; i < tb->numLines; i++)
406 free(tb->lines[i]);
407 free(tb->lines);
408 free(tb);
409 free(co);
410}