24
static char * expandTabs(const char * text);
25
static void textboxDraw(newtComponent co);
26
static void addLine(newtComponent co, const char * s, int len);
27
static void doReflow(const char * text, char ** resultPtr, int width,
28
int * badness, int * heightPtr);
29
static struct eventResult textboxEvent(newtComponent c,
31
static void textboxDestroy(newtComponent co);
32
static void textboxPlace(newtComponent co, int newLeft, int newTop);
33
static void textboxMapped(newtComponent co, int isMapped);
35
static struct componentOps textboxOps = {
43
static void textboxMapped(newtComponent co, int isMapped) {
44
struct textbox * tb = co->data;
46
co->isMapped = isMapped;
48
tb->sb->ops->mapped(tb->sb, isMapped);
51
static void textboxPlace(newtComponent co, int newLeft, int newTop) {
52
struct textbox * tb = co->data;
58
tb->sb->ops->place(tb->sb, co->left + co->width - 1, co->top);
61
void newtTextboxSetHeight(newtComponent co, int height) {
65
int newtTextboxGetNumLines(newtComponent co) {
66
struct textbox * tb = co->data;
68
return (tb->numLines);
71
newtComponent newtTextboxReflowed(int left, int top, char * text, int width,
72
int flexDown, int flexUp, int flags) {
75
int actWidth, actHeight;
77
reflowedText = newtReflowText(text, width, flexDown, flexUp,
78
&actWidth, &actHeight);
80
co = newtTextbox(left, top, actWidth, actHeight, NEWT_FLAG_WRAP);
81
newtTextboxSetText(co, reflowedText);
87
newtComponent newtTextbox(int left, int top, int width, int height, int flags) {
91
co = malloc(sizeof(*co));
92
tb = malloc(sizeof(*tb));
95
co->ops = &textboxOps;
103
tb->doWrap = flags & NEWT_FLAG_WRAP;
105
tb->linesAlloced = 0;
108
tb->textWidth = width;
110
if (flags & NEWT_FLAG_SCROLL) {
112
tb->sb = newtVerticalScrollbar(co->left + co->width - 1, co->top,
113
co->height, COLORSET_TEXTBOX, COLORSET_TEXTBOX);
121
static char * expandTabs(const char * text) {
122
int bufAlloced = strlen(text) + 40;
129
/* this one needs rewriting - Edmund */
130
buf = malloc(bufAlloced + 1);
131
for (src = text, dest = buf; *src; src++) {
132
if ((bufUsed + 10) > bufAlloced) {
133
bufAlloced += strlen(text) / 2;
134
buf = realloc(buf, bufAlloced + 1);
135
dest = buf + bufUsed;
138
i = 8 - (linePos & 8);
139
memset(dest, ' ', i);
140
dest += i, bufUsed += i, linePos += i;
158
static void doReflow(const char * text, char ** resultPtr, int width,
159
int * badness, int * heightPtr) {
166
int n, pos, resultn, i, j, k, w;
170
bufn = strlen(text) + 1000;
171
buf = malloc(bufn*sizeof(wchar_t));
175
resultn = strlen(text) + 1 + 1000;
176
result = malloc(resultn);
180
/* Convert the multibyte string to wide characters in buf */
181
memset(&mbstate, 0, sizeof(mbstate));
183
k = mbrtowc(&wc, text, MB_LEN_MAX, &mbstate);
184
if (!k || k == (size_t)(-2))
186
if (k == (size_t)(-1)) {
187
/* Replace invalid data with '?' */
194
buf = realloc(buf, bufn*sizeof(wchar_t));
199
memset(&mbstate, 0, sizeof(mbstate));
200
for (i = 0; i < n; ) {
202
for (j = i; j < n && buf[j] != '\n'; j++) {
203
if ((w = wcwidth(buf[j])) != -1)
204
if ((pos += w) > width)
207
if (j < n && buf[j] != '\n') {
208
for (k = j; i < k && !iswspace(buf[k]); k--) ;
209
for (; i < k && iswspace(buf[k-1]); k--) ;
210
j = (i == k) ? j : k;
213
for (k = i; k < j; k++) {
214
if ((w = wcwidth(buf[k])) != -1) {
216
if (nn + MB_LEN_MAX*2 + 1 > resultn) {
218
result = realloc(result, resultn);
220
nn += wcrtomb(result+nn, buf[k], &mbstate);
226
nn += wcrtomb(result+nn, '\n', &mbstate);
228
if (j < n && buf[j] != '\n')
229
howbad += width - pos;
230
else if (pos < width/2)
231
howbad += ((width)/2 - pos)/2;
232
if (j < n && buf[j] == '\n')
235
for (i = j; i < n && iswspace(buf[i]); i++) ;
241
if (badness) *badness = howbad;
242
if (resultPtr) *resultPtr = result;
243
if (heightPtr) *heightPtr = height;
248
#define iseuckanji(c) (0xa1 <= (unsigned char)(c&0xff) && (unsigned char)(c&0xff) <= 0xfe)
250
static void doReflow(const char * text, char ** resultPtr, int width,
251
int * badness, int * heightPtr) {
252
char * result = NULL;
253
const char * chptr, * end;
260
/* XXX I think this will work */
261
result = malloc(strlen(text) + (strlen(text) / width) + 2);
267
end = strchr(text, '\n');
269
end = text + strlen(text);
271
while (*text && text <= end) {
272
if (end - text < width) {
274
strncat(result, text, end - text);
275
strcat(result, "\n");
279
if (end - text < (width / 2))
280
howbad += ((width / 2) - (end - text)) / 2;
286
for ( i = 0; i < width - 1; i++ ) {
287
if ( !iseuckanji(*chptr)) {
289
} else if ( kanji == 1 ) {
297
while (chptr > text && !isspace(*chptr)) chptr--;
298
while (chptr > text && isspace(*chptr)) chptr--;
302
if (chptr-text == 1 && !isspace(*chptr))
303
chptr = text + width - 1;
306
howbad += width - (chptr - text) + 1;
309
strncat(result, text, chptr - text + 1 );
313
strncat(result, text, chptr - text );
315
strcat(result, "\n");
323
while (isspace(*text)) text++;
328
if (badness) *badness = howbad;
329
if (resultPtr) *resultPtr = result;
330
if (heightPtr) *heightPtr = height;
335
char * newtReflowText(char * text, int width, int flexDown, int flexUp,
336
int * actualWidth, int * actualHeight) {
340
int minbad, minbadwidth, howbad;
343
expandedText = expandTabs(text);
345
if (flexDown || flexUp) {
346
min = width - flexDown;
347
max = width + flexUp;
352
for (i = min; i <= max; i++) {
353
doReflow(expandedText, NULL, i, &howbad, NULL);
355
if (minbad == -1 || howbad < minbad) {
364
doReflow(expandedText, &result, width, NULL, actualHeight);
366
if (actualWidth) *actualWidth = width;
370
void newtTextboxSetText(newtComponent co, const char * text) {
371
const char * start, * end;
372
struct textbox * tb = co->data;
373
char * reflowed, * expanded;
374
int i, badness, height;
377
for (i = 0; i < tb->numLines; i++)
380
tb->linesAlloced = tb->numLines = 0;
383
expanded = expandTabs(text);
386
doReflow(expanded, &reflowed, tb->textWidth, &badness, &height);
391
for (start = expanded; *start; start++)
392
if (*start == '\n') tb->linesAlloced++;
394
/* This ++ leaves room for an ending line w/o a \n */
396
tb->lines = malloc(sizeof(char *) * tb->linesAlloced);
399
while ((end = strchr(start, '\n'))) {
400
addLine(co, start, end - start);
405
addLine(co, start, strlen(start));
410
/* This assumes the buffer is allocated properly! */
411
static void addLine(newtComponent co, const char * s, int len) {
412
struct textbox * tb = co->data;
420
memset(&mbstate, 0, sizeof(mbstate));
422
(k = mbrtowc(&wc, t, len, &mbstate))
426
&& (w = wcwidth(wc)) != -1
427
&& pos + w <= tb->textWidth;
428
t += k, len -= k, pos += w)
430
tb->lines[tb->numLines] = malloc(t - s + tb->textWidth - pos + 1);
431
memcpy(tb->lines[tb->numLines], s, t - s);
432
memset(tb->lines[tb->numLines] + (t - s), ' ', tb->textWidth - pos);
433
tb->lines[tb->numLines++][t - s + tb->textWidth - pos] = '\0';
435
if (len > tb->textWidth) len = tb->textWidth;
437
tb->lines[tb->numLines] = malloc(tb->textWidth + 1);
438
memset(tb->lines[tb->numLines], ' ', tb->textWidth);
439
memcpy(tb->lines[tb->numLines], s, len);
440
tb->lines[tb->numLines++][tb->textWidth] = '\0';
444
static void textboxDraw(newtComponent c) {
446
struct textbox * tb = c->data;
450
size = tb->numLines - c->height;
451
newtScrollbarSet(tb->sb, tb->topLine, size ? size : 0);
452
tb->sb->ops->draw(tb->sb);
455
newtColor(NEWT_COLORSET_TEXTBOX);
457
for (i = 0; (i + tb->topLine) < tb->numLines && i < c->height; i++) {
458
newtGotorc(c->top + i, c->left);
459
SLsmg_write_string(tb->lines[i + tb->topLine]);
463
static struct eventResult textboxEvent(newtComponent co,
465
struct textbox * tb = co->data;
466
struct eventResult er;
468
er.result = ER_IGNORED;
470
if (ev.when == EV_EARLY && ev.event == EV_KEYPRESS && tb->sb) {
473
if (tb->topLine) tb->topLine--;
475
er.result = ER_SWALLOWED;
479
if (tb->topLine < (tb->numLines - co->height)) tb->topLine++;
481
er.result = ER_SWALLOWED;
485
tb->topLine += co->height;
486
if (tb->topLine > (tb->numLines - co->height)) {
487
tb->topLine = tb->numLines - co->height;
488
if (tb->topLine < 0) tb->topLine = 0;
491
er.result = ER_SWALLOWED;
495
tb->topLine -= co->height;
496
if (tb->topLine < 0) tb->topLine = 0;
498
er.result = ER_SWALLOWED;
504
er.result = ER_SWALLOWED;
508
tb->topLine = tb->numLines - co->height;
509
if (tb->topLine < 0) tb->topLine = 0;
511
er.result = ER_SWALLOWED;
515
if (ev.when == EV_EARLY && ev.event == EV_MOUSE && tb->sb) {
516
/* Top scroll arrow */
517
if (ev.u.mouse.x == co->width && ev.u.mouse.y == co->top) {
518
if (tb->topLine) tb->topLine--;
521
er.result = ER_SWALLOWED;
523
/* Bottom scroll arrow */
524
if (ev.u.mouse.x == co->width &&
525
ev.u.mouse.y == co->top + co->height - 1) {
526
if (tb->topLine < (tb->numLines - co->height)) tb->topLine++;
529
er.result = ER_SWALLOWED;
535
static void textboxDestroy(newtComponent co) {
537
struct textbox * tb = co->data;
539
for (i = 0; i < tb->numLines; i++)