~ubuntu-branches/ubuntu/dapper/newt/dapper

« back to all changes in this revision

Viewing changes to textbox.c

  • Committer: Bazaar Package Importer
  • Author(s): Alastair McKinstry
  • Date: 2004-11-27 09:49:00 UTC
  • mfrom: (1.1.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20041127094900-mf7cbmb00g7t8xow
Tags: 0.51.6-20
Upgrade bug; lbnewt0.51 was supplying /usr/lib/libnewt.so in conflict
with libnewt-dev. Closes: #283185.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#include <ctype.h>
2
 
#include <slang.h>
3
 
#include <stdlib.h>
4
 
#include <string.h>
5
 
#ifdef UTF8
6
 
  #include <limits.h>
7
 
  #include <wchar.h>
8
 
  #include <wctype.h>
9
 
#endif
10
 
 
11
 
#include "newt.h"
12
 
#include "newt_pr.h"
13
 
 
14
 
struct textbox {
15
 
    char ** lines;
16
 
    int numLines;
17
 
    int linesAlloced;
18
 
    int doWrap;
19
 
    newtComponent sb;
20
 
    int topLine;
21
 
    int textWidth;
22
 
};
23
 
 
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,
30
 
                                      struct event ev);
31
 
static void textboxDestroy(newtComponent co);
32
 
static void textboxPlace(newtComponent co, int newLeft, int newTop);
33
 
static void textboxMapped(newtComponent co, int isMapped);
34
 
 
35
 
static struct componentOps textboxOps = {
36
 
    textboxDraw,
37
 
    textboxEvent,
38
 
    textboxDestroy,
39
 
    textboxPlace,
40
 
    textboxMapped,
41
 
} ;
42
 
 
43
 
static void textboxMapped(newtComponent co, int isMapped) {
44
 
    struct textbox * tb = co->data;
45
 
 
46
 
    co->isMapped = isMapped;
47
 
    if (tb->sb)
48
 
        tb->sb->ops->mapped(tb->sb, isMapped);
49
 
}
50
 
 
51
 
static void textboxPlace(newtComponent co, int newLeft, int newTop) {
52
 
    struct textbox * tb = co->data;
53
 
 
54
 
    co->top = newTop;
55
 
    co->left = newLeft;
56
 
 
57
 
    if (tb->sb)
58
 
        tb->sb->ops->place(tb->sb, co->left + co->width - 1, co->top);
59
 
}
60
 
 
61
 
void newtTextboxSetHeight(newtComponent co, int height) {
62
 
    co->height = height;
63
 
}
64
 
 
65
 
int newtTextboxGetNumLines(newtComponent co) {
66
 
    struct textbox * tb = co->data;
67
 
 
68
 
    return (tb->numLines);
69
 
}
70
 
 
71
 
newtComponent newtTextboxReflowed(int left, int top, char * text, int width,
72
 
                                  int flexDown, int flexUp, int flags) {
73
 
    newtComponent co;
74
 
    char * reflowedText;
75
 
    int actWidth, actHeight;
76
 
 
77
 
    reflowedText = newtReflowText(text, width, flexDown, flexUp,
78
 
                                  &actWidth, &actHeight);
79
 
    
80
 
    co = newtTextbox(left, top, actWidth, actHeight, NEWT_FLAG_WRAP);
81
 
    newtTextboxSetText(co, reflowedText);
82
 
    free(reflowedText);
83
 
 
84
 
    return co;
85
 
}
86
 
 
87
 
newtComponent newtTextbox(int left, int top, int width, int height, int flags) {
88
 
    newtComponent co;
89
 
    struct textbox * tb;
90
 
 
91
 
    co = malloc(sizeof(*co));
92
 
    tb = malloc(sizeof(*tb));
93
 
    co->data = tb;
94
 
 
95
 
    co->ops = &textboxOps;
96
 
 
97
 
    co->height = height;
98
 
    co->top = top;
99
 
    co->left = left;
100
 
    co->takesFocus = 0;
101
 
    co->width = width;
102
 
 
103
 
    tb->doWrap = flags & NEWT_FLAG_WRAP;
104
 
    tb->numLines = 0;
105
 
    tb->linesAlloced = 0;
106
 
    tb->lines = NULL;
107
 
    tb->topLine = 0;
108
 
    tb->textWidth = width;
109
 
 
110
 
    if (flags & NEWT_FLAG_SCROLL) {
111
 
        co->width += 2;
112
 
        tb->sb = newtVerticalScrollbar(co->left + co->width - 1, co->top, 
113
 
                           co->height, COLORSET_TEXTBOX, COLORSET_TEXTBOX);
114
 
    } else {
115
 
        tb->sb = NULL;
116
 
    }
117
 
 
118
 
    return co;
119
 
}
120
 
 
121
 
static char * expandTabs(const char * text) {
122
 
    int bufAlloced = strlen(text) + 40;
123
 
    char * buf, * dest;
124
 
    const char * src;
125
 
    int bufUsed = 0;
126
 
    int linePos = 0;
127
 
    int i;
128
 
 
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;
136
 
        }
137
 
        if (*src == '\t') {
138
 
            i = 8 - (linePos & 8);
139
 
            memset(dest, ' ', i);
140
 
            dest += i, bufUsed += i, linePos += i;
141
 
        } else {
142
 
            if (*src == '\n')
143
 
                linePos = 0;
144
 
            else
145
 
                linePos++;
146
 
 
147
 
            *dest++ = *src;
148
 
            bufUsed++;
149
 
        }
150
 
    }
151
 
 
152
 
    *dest = '\0';
153
 
    return buf;
154
 
}
155
 
 
156
 
#ifdef UTF8
157
 
 
158
 
static void doReflow(const char * text, char ** resultPtr, int width, 
159
 
                    int * badness, int * heightPtr) {
160
 
    wchar_t * buf;
161
 
    int bufn;
162
 
    int nn = 0;
163
 
    char * result = 0;
164
 
    int howbad = 0;
165
 
    int height = 0;
166
 
    int n, pos, resultn, i, j, k, w;
167
 
    wchar_t wc;
168
 
    mbstate_t mbstate;
169
 
 
170
 
    bufn = strlen(text) + 1000;
171
 
    buf = malloc(bufn*sizeof(wchar_t));
172
 
    n = 0;
173
 
 
174
 
    if (resultPtr) {
175
 
       resultn = strlen(text) + 1 + 1000;
176
 
       result = malloc(resultn);
177
 
       nn = 0;
178
 
    }
179
 
 
180
 
    /* Convert the multibyte string to wide characters in buf */
181
 
    memset(&mbstate, 0, sizeof(mbstate));
182
 
    for (;;) {
183
 
        k = mbrtowc(&wc, text, MB_LEN_MAX, &mbstate);
184
 
        if (!k || k == (size_t)(-2))
185
 
            break;
186
 
        if (k == (size_t)(-1)) {
187
 
            /* Replace invalid data with '?' */
188
 
            k = 1;
189
 
            wc = '?';
190
 
        }
191
 
       text += k;
192
 
       if (n >= bufn) {
193
 
           bufn += width;
194
 
           buf = realloc(buf, bufn*sizeof(wchar_t));
195
 
       }
196
 
       buf[n++] = wc;
197
 
    }
198
 
 
199
 
    memset(&mbstate, 0, sizeof(mbstate));
200
 
    for (i = 0; i < n; ) {
201
 
       pos = 0;
202
 
       for (j = i; j < n && buf[j] != '\n'; j++) {
203
 
           if ((w = wcwidth(buf[j])) != -1)
204
 
               if ((pos += w) > width)
205
 
                   break;
206
 
       }
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;
211
 
       }
212
 
       pos = 0;
213
 
       for (k = i; k < j; k++) {
214
 
           if ((w = wcwidth(buf[k])) != -1) {
215
 
               if (result) {
216
 
                   if (nn + MB_LEN_MAX*2 + 1 > resultn) {
217
 
                       resultn += width;
218
 
                       result = realloc(result, resultn);
219
 
                   }
220
 
                   nn += wcrtomb(result+nn, buf[k], &mbstate);
221
 
               }
222
 
               pos += w;
223
 
           }
224
 
       }
225
 
       if (result)
226
 
           nn += wcrtomb(result+nn, '\n', &mbstate);
227
 
       ++height;
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')
233
 
           i = j + 1;
234
 
       else
235
 
           for (i = j; i < n && iswspace(buf[i]); i++) ;
236
 
    }
237
 
 
238
 
    if (result)
239
 
       result[nn] = '\0';
240
 
    free(buf);
241
 
    if (badness) *badness = howbad;
242
 
    if (resultPtr) *resultPtr = result;
243
 
    if (heightPtr) *heightPtr = height;
244
 
}
245
 
 
246
 
#else /* UTF8 */
247
 
 
248
 
#define iseuckanji(c)   (0xa1 <= (unsigned char)(c&0xff) && (unsigned char)(c&0xff) <= 0xfe)
249
 
 
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;
254
 
    int i;
255
 
    int howbad = 0;
256
 
    int height = 0;
257
 
    int kanji = 0;
258
 
 
259
 
    if (resultPtr) {
260
 
        /* XXX I think this will work */
261
 
        result = malloc(strlen(text) + (strlen(text) / width) + 2);
262
 
        *result = '\0';
263
 
    }
264
 
    
265
 
    while (*text) {
266
 
        kanji = 0;
267
 
        end = strchr(text, '\n');
268
 
        if (!end)
269
 
            end = text + strlen(text);
270
 
 
271
 
        while (*text && text <= end) {
272
 
            if (end - text < width) {
273
 
                if (result) {
274
 
                    strncat(result, text, end - text);
275
 
                    strcat(result, "\n");
276
 
                    height++;
277
 
                }
278
 
 
279
 
                if (end - text < (width / 2))
280
 
                    howbad += ((width / 2) - (end - text)) / 2;
281
 
                text = end;
282
 
                if (*text) text++;
283
 
            } else {
284
 
                chptr = text;
285
 
                kanji = 0;
286
 
                for ( i = 0; i < width - 1; i++ ) {
287
 
                    if ( !iseuckanji(*chptr)) {
288
 
                        kanji = 0;
289
 
                    } else if ( kanji == 1 ) {
290
 
                        kanji = 2; 
291
 
                    } else {
292
 
                        kanji = 1;
293
 
                    }
294
 
                    chptr++;
295
 
                }
296
 
                if (kanji == 0) {
297
 
                    while (chptr > text && !isspace(*chptr)) chptr--;
298
 
                    while (chptr > text && isspace(*chptr)) chptr--;
299
 
                    chptr++;
300
 
                }
301
 
                
302
 
                if (chptr-text == 1 && !isspace(*chptr))
303
 
                  chptr = text + width - 1;
304
 
 
305
 
                if (chptr > text)
306
 
                    howbad += width - (chptr - text) + 1;
307
 
                if (result) {
308
 
                  if (kanji == 1) {
309
 
                    strncat(result, text, chptr - text + 1 );
310
 
                    chptr++;
311
 
                    kanji = 0;
312
 
                  } else {
313
 
                    strncat(result, text, chptr - text );
314
 
                  }
315
 
                    strcat(result, "\n");
316
 
                    height++;
317
 
                }
318
 
 
319
 
                if (isspace(*chptr))
320
 
                    text = chptr + 1;
321
 
                else
322
 
                  text = chptr;
323
 
                while (isspace(*text)) text++;
324
 
            }
325
 
        }
326
 
    }
327
 
 
328
 
    if (badness) *badness = howbad;
329
 
    if (resultPtr) *resultPtr = result;
330
 
    if (heightPtr) *heightPtr = height;
331
 
}
332
 
 
333
 
#endif
334
 
 
335
 
char * newtReflowText(char * text, int width, int flexDown, int flexUp,
336
 
                      int * actualWidth, int * actualHeight) {
337
 
    int min, max;
338
 
    int i;
339
 
    char * result;
340
 
    int minbad, minbadwidth, howbad;
341
 
    char * expandedText;
342
 
 
343
 
    expandedText = expandTabs(text);
344
 
 
345
 
    if (flexDown || flexUp) {
346
 
        min = width - flexDown;
347
 
        max = width + flexUp;
348
 
 
349
 
        minbad = -1;
350
 
        minbadwidth = width;
351
 
 
352
 
        for (i = min; i <= max; i++) {
353
 
            doReflow(expandedText, NULL, i, &howbad, NULL);
354
 
 
355
 
            if (minbad == -1 || howbad < minbad) {
356
 
                minbad = howbad;
357
 
                minbadwidth = i;
358
 
            }
359
 
        }
360
 
 
361
 
        width = minbadwidth;
362
 
    }
363
 
 
364
 
    doReflow(expandedText, &result, width, NULL, actualHeight);
365
 
    free(expandedText);
366
 
    if (actualWidth) *actualWidth = width;
367
 
    return result;
368
 
}
369
 
 
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;
375
 
 
376
 
    if (tb->lines) {
377
 
        for (i = 0; i < tb->numLines; i++)
378
 
            free(tb->lines[i]);
379
 
        free(tb->lines);
380
 
        tb->linesAlloced = tb->numLines = 0;
381
 
    }
382
 
 
383
 
    expanded = expandTabs(text);
384
 
 
385
 
    if (tb->doWrap) {
386
 
        doReflow(expanded, &reflowed, tb->textWidth, &badness, &height);
387
 
        free(expanded);
388
 
        expanded = reflowed;
389
 
    }
390
 
 
391
 
    for (start = expanded; *start; start++)
392
 
        if (*start == '\n') tb->linesAlloced++;
393
 
 
394
 
    /* This ++ leaves room for an ending line w/o a \n */
395
 
    tb->linesAlloced++;
396
 
    tb->lines = malloc(sizeof(char *) * tb->linesAlloced);
397
 
 
398
 
    start = expanded;
399
 
    while ((end = strchr(start, '\n'))) {
400
 
        addLine(co, start, end - start);
401
 
        start = end + 1;
402
 
    }
403
 
 
404
 
    if (*start)
405
 
        addLine(co, start, strlen(start));
406
 
 
407
 
    free(expanded);
408
 
}
409
 
 
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;
413
 
 
414
 
#ifdef UTF8
415
 
    const char *t;
416
 
    wchar_t wc;
417
 
    int k, w, pos = 0;
418
 
    mbstate_t mbstate;
419
 
 
420
 
    memset(&mbstate, 0, sizeof(mbstate));
421
 
    for (t = s;
422
 
         (k = mbrtowc(&wc, t, len, &mbstate))
423
 
            && k != (size_t)(-1)
424
 
            && k != (size_t)(-2)
425
 
            && wc
426
 
            && (w = wcwidth(wc)) != -1
427
 
            && pos + w <= tb->textWidth;
428
 
        t += k, len -= k, pos += w)
429
 
       ;
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';
434
 
#else
435
 
    if (len > tb->textWidth) len = tb->textWidth;
436
 
 
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';
441
 
#endif
442
 
}
443
 
 
444
 
static void textboxDraw(newtComponent c) {
445
 
    int i;
446
 
    struct textbox * tb = c->data;
447
 
    int size;
448
 
 
449
 
    if (tb->sb) {
450
 
        size = tb->numLines - c->height;
451
 
        newtScrollbarSet(tb->sb, tb->topLine, size ? size : 0);
452
 
        tb->sb->ops->draw(tb->sb);
453
 
    }
454
 
 
455
 
    newtColor(NEWT_COLORSET_TEXTBOX);
456
 
   
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]);
460
 
    }
461
 
}
462
 
 
463
 
static struct eventResult textboxEvent(newtComponent co, 
464
 
                                      struct event ev) {
465
 
    struct textbox * tb = co->data;
466
 
    struct eventResult er;
467
 
 
468
 
    er.result = ER_IGNORED;
469
 
 
470
 
    if (ev.when == EV_EARLY && ev.event == EV_KEYPRESS && tb->sb) {
471
 
        switch (ev.u.key) {
472
 
          case NEWT_KEY_UP:
473
 
            if (tb->topLine) tb->topLine--;
474
 
            textboxDraw(co);
475
 
            er.result = ER_SWALLOWED;
476
 
            break;
477
 
 
478
 
          case NEWT_KEY_DOWN:
479
 
            if (tb->topLine < (tb->numLines - co->height)) tb->topLine++;
480
 
            textboxDraw(co);
481
 
            er.result = ER_SWALLOWED;
482
 
            break;
483
 
 
484
 
          case NEWT_KEY_PGDN:
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;
489
 
            }
490
 
            textboxDraw(co);
491
 
            er.result = ER_SWALLOWED;
492
 
            break;
493
 
 
494
 
          case NEWT_KEY_PGUP:
495
 
            tb->topLine -= co->height;
496
 
            if (tb->topLine < 0) tb->topLine = 0;
497
 
            textboxDraw(co);
498
 
            er.result = ER_SWALLOWED;
499
 
            break;
500
 
 
501
 
          case NEWT_KEY_HOME:
502
 
            tb->topLine = 0;
503
 
            textboxDraw(co);
504
 
            er.result = ER_SWALLOWED;
505
 
            break;
506
 
 
507
 
          case NEWT_KEY_END:
508
 
            tb->topLine = tb->numLines - co->height;
509
 
            if (tb->topLine < 0) tb->topLine = 0;
510
 
            textboxDraw(co);
511
 
            er.result = ER_SWALLOWED;
512
 
            break;
513
 
        }
514
 
    }
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--;
519
 
            textboxDraw(co);
520
 
            
521
 
            er.result = ER_SWALLOWED;
522
 
        }
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++;
527
 
            textboxDraw(co);
528
 
            
529
 
            er.result = ER_SWALLOWED;
530
 
        }
531
 
    }
532
 
    return er;
533
 
}
534
 
 
535
 
static void textboxDestroy(newtComponent co) {
536
 
    int i;
537
 
    struct textbox * tb = co->data;
538
 
 
539
 
    for (i = 0; i < tb->numLines; i++) 
540
 
        free(tb->lines[i]);
541
 
    free(tb->lines);
542
 
    free(tb);
543
 
    free(co);
544
 
}