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