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 
newtEntrySet(newtComponent co,const char * value,int cursorAtEnd)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 
newtEntry(int left,int top,const char * initialValue,int width,const char ** resultPtr,int flags)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 
scroll(struct entry * en,int width)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 
entryDraw(newtComponent co)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 
newtEntrySetFlags(newtComponent co,int flags,enum newtFlagsSense sense)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 
newtEntrySetColors(newtComponent co,int normal,int disabled)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 
entryDestroy(newtComponent co)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 
entryEvent(newtComponent co,struct event ev)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 
previous_char(const char * buf,int pos)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 
next_char(const char * buf,int pos)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 
entryHandleKey(newtComponent co,int key)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 
newtEntryGetValue(newtComponent co)496 char * newtEntryGetValue(newtComponent co) {
497     struct entry * en = co->data;
498 
499     return en->buf;
500 }
501 
newtEntrySetFilter(newtComponent co,newtEntryFilter filter,void * data)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 
newtEntryGetCursorPosition(newtComponent co)508 int newtEntryGetCursorPosition (newtComponent co) {
509     struct entry * en = co->data;
510 
511     return en->cursorPosition;
512 }
513 
newtEntrySetCursorPosition(newtComponent co,int position)514 void newtEntrySetCursorPosition (newtComponent co, int position) {
515     struct entry * en = co->data;
516 
517     en->cursorPosition = position;
518 }
519