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