]> git.ipfire.org Git - thirdparty/newt.git/blob - entry.c
0.52.24
[thirdparty/newt.git] / entry.c
1 #include "config.h"
2
3 #ifdef HAVE_ALLOCA_H
4 #include <alloca.h>
5 #endif
6
7 #include <ctype.h>
8 #include <slang.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <wchar.h>
12
13 #include "newt.h"
14 #include "newt_pr.h"
15
16 struct entry {
17 int flags;
18 char * buf;
19 const char ** resultPtr;
20 int bufAlloced;
21 int bufUsed; /* amount of the buffer that's been used */
22 int cursorPosition; /* cursor *in the string* on on screen */
23 int firstChar; /* first character position being shown */
24 newtEntryFilter filter;
25 void * filterData;
26 int cs;
27 int csDisabled;
28 };
29
30 static int previous_char(const char *buf, int pos);
31 static int next_char(const char *buf, int pos);
32 static void entryDraw(newtComponent co);
33 static void entryDestroy(newtComponent co);
34 static struct eventResult entryEvent(newtComponent co,
35 struct event ev);
36
37 static struct eventResult entryHandleKey(newtComponent co, int key);
38
39 static struct componentOps entryOps = {
40 entryDraw,
41 entryEvent,
42 entryDestroy,
43 newtDefaultPlaceHandler,
44 newtDefaultMappedHandler,
45 } ;
46
47 void newtEntrySet(newtComponent co, const char * value, int cursorAtEnd) {
48 struct entry * en = co->data;
49
50 if ((strlen(value) + 1) > (unsigned int)en->bufAlloced) {
51 free(en->buf);
52 en->bufAlloced = strlen(value) + 1;
53 en->buf = malloc(en->bufAlloced);
54 if (en->resultPtr) *en->resultPtr = en->buf;
55 }
56 memset(en->buf, 0, en->bufAlloced); /* clear the buffer */
57 strcpy(en->buf, value);
58 en->bufUsed = strlen(value);
59 en->firstChar = 0;
60 if (cursorAtEnd)
61 en->cursorPosition = en->bufUsed;
62 else
63 en->cursorPosition = 0;
64
65 entryDraw(co);
66 } ;
67
68 newtComponent newtEntry(int left, int top, const char * initialValue, int width,
69 const char ** resultPtr, int flags) {
70 newtComponent co;
71 struct entry * en;
72
73 co = malloc(sizeof(*co));
74 en = malloc(sizeof(struct entry));
75 co->data = en;
76
77 co->top = top;
78 co->left = left;
79 co->height = 1;
80 co->width = width;
81 co->isMapped = 0;
82 co->callback = NULL;
83 co->destroyCallback = NULL;
84
85 co->ops = &entryOps;
86
87 en->flags = flags;
88 en->cursorPosition = 0;
89 en->firstChar = 0;
90 en->bufUsed = 0;
91 en->bufAlloced = width + 1;
92 en->filter = NULL;
93
94 if (!(en->flags & NEWT_FLAG_DISABLED))
95 co->takesFocus = 1;
96 else
97 co->takesFocus = 0;
98
99 if (initialValue && strlen(initialValue) > (unsigned int)width) {
100 en->bufAlloced = strlen(initialValue) + 1;
101 }
102 en->buf = malloc(en->bufAlloced);
103 en->resultPtr = resultPtr;
104 if (en->resultPtr) *en->resultPtr = en->buf;
105
106 memset(en->buf, 0, en->bufAlloced);
107 if (initialValue) {
108 strcpy(en->buf, initialValue);
109 en->bufUsed = strlen(initialValue);
110 en->cursorPosition = en->bufUsed;
111
112 /* move cursor back if entry is full */
113 if (en->cursorPosition && !(en->flags & NEWT_FLAG_SCROLL ||
114 wstrlen(en->buf, -1) < co->width))
115 en->cursorPosition = previous_char(en->buf, en->cursorPosition);
116 } else {
117 *en->buf = '\0';
118 en->bufUsed = 0;
119 en->cursorPosition = 0;
120 }
121
122 en->cs = NEWT_COLORSET_ENTRY;
123 en->csDisabled = NEWT_COLORSET_DISENTRY;
124
125 return co;
126 }
127
128 static void scroll(struct entry *en, int width)
129 {
130 int r, lv, rv, cntx, cw, cn, nc, pc, ncw, pcw;
131
132 if (width <= 1) {
133 en->firstChar = en->cursorPosition;
134 return;
135 }
136
137 cntx = width / 4;
138 if (cntx > 5)
139 cntx = 5;
140
141 if (en->cursorPosition < en->firstChar)
142 en->firstChar = en->cursorPosition;
143
144 cn = next_char(en->buf, en->cursorPosition);
145 cw = en->cursorPosition >= en->bufUsed ? 1 :
146 wstrlen(en->buf + en->cursorPosition, cn - en->cursorPosition);
147
148 r = wstrlen(en->buf + cn, -1);
149
150 lv = wstrlen(en->buf + en->firstChar, en->cursorPosition - en->firstChar);
151 rv = width - lv - cw;
152
153 #define RC (ncw > 0 && (r > rv && lv - ncw >= cntx && rv < cntx))
154 #define LC (pcw > 0 && (r + pcw <= rv || (lv < cntx && rv - pcw >= cntx)))
155
156 nc = next_char(en->buf, en->firstChar);
157 ncw = wstrlen(en->buf + en->firstChar, nc - en->firstChar);
158 if (RC) {
159 do {
160 lv -= ncw;
161 rv += ncw;
162 en->firstChar = nc;
163 nc = next_char(en->buf, en->firstChar);
164 ncw = wstrlen(en->buf + en->firstChar, nc - en->firstChar);
165 } while (RC);
166 return;
167 }
168
169 pc = previous_char(en->buf, en->firstChar);
170 pcw = wstrlen(en->buf + pc, en->firstChar - pc);
171 if (LC) {
172 do {
173 lv += pcw;
174 rv -= pcw;
175 en->firstChar = pc;
176 pc = previous_char(en->buf, en->firstChar);
177 pcw = wstrlen(en->buf + pc, en->firstChar - pc);
178 } while (LC);
179 }
180 }
181
182 static void entryDraw(newtComponent co) {
183 struct entry * en = co->data;
184 int i;
185 char * chptr;
186 int len;
187 char *tmpptr = NULL;
188
189 if (!co->isMapped) return;
190
191 if (en->flags & NEWT_FLAG_DISABLED)
192 SLsmg_set_color(en->csDisabled);
193 else
194 SLsmg_set_color(en->cs);
195
196 if (en->flags & NEWT_FLAG_HIDDEN) {
197 newtGotorc(co->top, co->left);
198 for (i = 0; i < co->width; i++)
199 SLsmg_write_char('_');
200 newtGotorc(co->top, co->left);
201
202 return;
203 }
204
205 newtTrashScreen();
206
207 /* scroll if necessary */
208 scroll(en, co->width);
209
210 chptr = en->buf + en->firstChar;
211
212 if (en->flags & NEWT_FLAG_PASSWORD) {
213 len = wstrlen(chptr, -1);
214 tmpptr = alloca(len + 1);
215 for (i = 0; i < len; i++)
216 memset(tmpptr, '*', len);
217 tmpptr[len] = '\0';
218 chptr = tmpptr;
219 }
220
221 len = wstrlen(chptr, -1);
222
223 /* workaround for double width characters */
224 if (co->width > 1) {
225 i = len < co->width ? len : co->width;
226 i = i > 2 ? i - 2 : 0;
227 newtGotorc(co->top, co->left + i);
228 SLsmg_write_char('_');
229 SLsmg_write_char('_');
230 }
231
232 newtGotorc(co->top, co->left);
233
234 if (len <= co->width) {
235 i = len;
236 SLsmg_write_string(chptr);
237 while (i < co->width) {
238 SLsmg_write_char('_');
239 i++;
240 }
241 } else
242 SLsmg_write_nstring(chptr, co->width);
243
244 newtGotorc(co->top, co->left + wstrlen(en->buf+en->firstChar, en->cursorPosition - en->firstChar));
245 }
246
247 void newtEntrySetFlags(newtComponent co, int flags, enum newtFlagsSense sense) {
248 struct entry * en = co->data;
249 int row, col;
250
251 en->flags = newtSetFlags(en->flags, flags, sense);
252
253 if (!(en->flags & NEWT_FLAG_DISABLED))
254 co->takesFocus = 1;
255 else
256 co->takesFocus = 0;
257
258 newtGetrc(&row, &col);
259 entryDraw(co);
260 newtGotorc(row, col);
261 }
262
263 void newtEntrySetColors(newtComponent co, int normal, int disabled) {
264 struct entry * en = co->data;
265
266 en->cs = normal;
267 en->csDisabled = disabled;
268 entryDraw(co);
269 }
270
271 static void entryDestroy(newtComponent co) {
272 struct entry * en = co->data;
273
274 free(en->buf);
275 free(en);
276 free(co);
277 }
278
279 static struct eventResult entryEvent(newtComponent co,
280 struct event ev) {
281 struct entry * en = co->data;
282 struct eventResult er;
283 int ch;
284
285 er.result = ER_IGNORED;
286
287 if (ev.when == EV_NORMAL) {
288 switch (ev.event) {
289 case EV_FOCUS:
290 newtCursorOn();
291 if (en->flags & NEWT_FLAG_HIDDEN)
292 newtGotorc(co->top, co->left);
293 else
294 newtGotorc(co->top, co->left +
295 wstrlen(en->buf + en->firstChar, en->cursorPosition - en->firstChar));
296 er.result = ER_SWALLOWED;
297 break;
298
299 case EV_UNFOCUS:
300 newtCursorOff();
301 newtGotorc(0, 0);
302 er.result = ER_SWALLOWED;
303 if (co->callback)
304 co->callback(co, co->callbackData);
305 break;
306
307 case EV_KEYPRESS:
308 ch = ev.u.key;
309 if (en->filter)
310 ch = en->filter(co, en->filterData, ch, en->cursorPosition);
311 if (ch) er = entryHandleKey(co, ch);
312 break;
313
314 case EV_MOUSE:
315 if ((ev.u.mouse.type == MOUSE_BUTTON_DOWN) &&
316 (en->flags ^ NEWT_FLAG_HIDDEN)) {
317 if (strlen(en->buf) >= ev.u.mouse.x - co->left) {
318 en->cursorPosition = ev.u.mouse.x - co->left;
319 newtGotorc(co->top,
320 co->left +(en->cursorPosition - en->firstChar));
321 } else {
322 en->cursorPosition = strlen(en->buf);
323 newtGotorc(co->top,
324 co->left +(en->cursorPosition - en->firstChar));
325 }
326 }
327 break;
328 }
329 }
330
331 return er;
332 }
333
334 static int previous_char(const char *buf, int pos)
335 {
336 int len = 0;
337 int off = 0;
338
339 while (off < pos) {
340 len = mblen(buf+off, MB_CUR_MAX);
341 if (len <= 0)
342 return pos;
343 off+=len;
344 }
345 return off-len;
346 }
347
348 static int next_char(const char *buf, int pos)
349 {
350 int len = mblen(buf + pos, MB_CUR_MAX);
351 if (len <= 0)
352 return pos;
353 return pos+len;
354 }
355
356 static struct eventResult entryHandleKey(newtComponent co, int key) {
357 struct entry * en = co->data;
358 struct eventResult er;
359 char * chptr;
360
361 er.result = ER_SWALLOWED;
362 switch (key) {
363 case '\r': /* Return */
364 if (en->flags & NEWT_FLAG_RETURNEXIT) {
365 newtCursorOff();
366 er.result = ER_EXITFORM;
367 } else {
368 er.result = ER_NEXTCOMP;
369 }
370 break;
371
372 case '\001': /* ^A */
373 case NEWT_KEY_HOME:
374 en->cursorPosition = 0;
375 break;
376
377 case '\005': /* ^E */
378 case NEWT_KEY_END:
379 en->cursorPosition = en->bufUsed;
380 break;
381
382 case '\013': /* ^K */
383 en->bufUsed = en->cursorPosition;
384 memset(en->buf + en->bufUsed, 0, en->bufAlloced - en->bufUsed);
385 break;
386
387 case '\025': /* ^U */
388 en->bufUsed -= en->cursorPosition;
389 memmove(en->buf, en->buf + en->cursorPosition, en->bufUsed);
390 en->cursorPosition = 0;
391 memset(en->buf + en->bufUsed, 0, en->bufAlloced - en->bufUsed);
392 break;
393
394 case '\002': /* ^B */
395 case NEWT_KEY_LEFT:
396 if (en->cursorPosition)
397 en->cursorPosition = previous_char(en->buf, en->cursorPosition);
398 break;
399
400 case '\004':
401 case NEWT_KEY_DELETE:
402 chptr = en->buf + en->cursorPosition;
403 if (*chptr) {
404 int delta = next_char(en->buf, en->cursorPosition)-en->cursorPosition;
405 if (delta) {
406 chptr+=delta;
407 while (*chptr) {
408 *(chptr - delta) = *chptr;
409 chptr++;
410 }
411 memset(chptr - delta, 0, delta);
412 en->bufUsed-=delta;
413 }
414 }
415 break;
416
417 case NEWT_KEY_BKSPC: {
418 int prev = previous_char(en->buf, en->cursorPosition);
419 if (en->cursorPosition != prev) {
420 /* if this isn't true, there's nothing to erase */
421 int delta = en->cursorPosition - prev;
422 chptr = en->buf + en->cursorPosition;
423 en->bufUsed-=delta;
424 en->cursorPosition-=delta;
425 while (*chptr) {
426 *(chptr - delta) = *chptr;
427 chptr++;
428 }
429 memset(chptr - delta, 0, delta);
430 }
431 }
432 break;
433
434 case '\006': /* ^B */
435 case NEWT_KEY_RIGHT:
436 if (en->cursorPosition < en->bufUsed)
437 en->cursorPosition = next_char(en->buf, en->cursorPosition);
438 break;
439
440 default:
441 if ((key >= 0x20 && key <= 0x7e) || (key >= 0x80 && key <= 0xff)) {
442 char s[MB_CUR_MAX];
443 mbstate_t ps;
444 int i, l;
445
446 for (i = 1, s[0] = key; ; i++) {
447 memset(&ps, 0, sizeof (ps));
448 l = mbrtowc(NULL, s, i, &ps);
449 if (l == -1) /* invalid sequence */
450 i = 0;
451 if (l != -2) /* not incomplete sequence */
452 break;
453
454 /* read next byte */
455 if (i == MB_CUR_MAX || !SLang_input_pending(1)) {
456 i = 0;
457 break;
458 }
459 s[i] = SLang_getkey();
460 }
461
462 if (!i || (!(en->flags & NEWT_FLAG_SCROLL) && wstrlen(en->buf, -1) + wstrlen(s, i) > co->width)) {
463 /* FIXME this is broken */
464 SLtt_beep();
465 break;
466 }
467
468 if ((en->bufUsed + i) >= en->bufAlloced) {
469 en->bufAlloced += 20;
470 en->buf = realloc(en->buf, en->bufAlloced);
471 if (en->resultPtr) *en->resultPtr = en->buf;
472 memset(en->buf + en->bufAlloced - 20, 0, 20);
473 }
474
475 if (en->cursorPosition != en->bufUsed) {
476 /* insert the new character */
477 memmove(en->buf + en->cursorPosition + i, en->buf + en->cursorPosition, en->bufUsed - en->cursorPosition);
478 }
479 en->bufUsed += i;
480 for (l = 0; l < i; l++)
481 en->buf[en->cursorPosition++] = s[l];
482 } else {
483 er.result = ER_IGNORED;
484 }
485 }
486
487 if (en->cursorPosition == en->bufUsed && en->cursorPosition &&
488 !(en->flags & NEWT_FLAG_SCROLL || wstrlen(en->buf, -1) < co->width))
489 en->cursorPosition = previous_char(en->buf, en->cursorPosition);
490
491 entryDraw(co);
492
493 return er;
494 }
495
496 char * newtEntryGetValue(newtComponent co) {
497 struct entry * en = co->data;
498
499 return en->buf;
500 }
501
502 void newtEntrySetFilter(newtComponent co, newtEntryFilter filter, void * data) {
503 struct entry * en = co->data;
504 en->filter = filter;
505 en->filterData = data;
506 }
507
508 int newtEntryGetCursorPosition (newtComponent co) {
509 struct entry * en = co->data;
510
511 return en->cursorPosition;
512 }
513
514 void newtEntrySetCursorPosition (newtComponent co, int position) {
515 struct entry * en = co->data;
516
517 en->cursorPosition = position;
518 }