#include "config.h" #ifdef HAVE_ALLOCA_H #include #endif #include #include #include #include #include #include "newt.h" #include "newt_pr.h" struct entry { int flags; char * buf; const char ** resultPtr; int bufAlloced; int bufUsed; /* amount of the buffer that's been used */ int cursorPosition; /* cursor *in the string* on on screen */ int firstChar; /* first character position being shown */ newtEntryFilter filter; void * filterData; int cs; int csDisabled; }; static int previous_char(const char *buf, int pos); static int next_char(const char *buf, int pos); static void entryDraw(newtComponent co); static void entryDestroy(newtComponent co); static struct eventResult entryEvent(newtComponent co, struct event ev); static struct eventResult entryHandleKey(newtComponent co, int key); static struct componentOps entryOps = { entryDraw, entryEvent, entryDestroy, newtDefaultPlaceHandler, newtDefaultMappedHandler, } ; void newtEntrySet(newtComponent co, const char * value, int cursorAtEnd) { struct entry * en = co->data; if ((strlen(value) + 1) > (unsigned int)en->bufAlloced) { free(en->buf); en->bufAlloced = strlen(value) + 1; en->buf = malloc(en->bufAlloced); if (en->resultPtr) *en->resultPtr = en->buf; } memset(en->buf, 0, en->bufAlloced); /* clear the buffer */ strcpy(en->buf, value); en->bufUsed = strlen(value); en->firstChar = 0; if (cursorAtEnd) en->cursorPosition = en->bufUsed; else en->cursorPosition = 0; entryDraw(co); } ; newtComponent newtEntry(int left, int top, const char * initialValue, int width, const char ** resultPtr, int flags) { newtComponent co; struct entry * en; co = malloc(sizeof(*co)); en = malloc(sizeof(struct entry)); co->data = en; co->top = top; co->left = left; co->height = 1; co->width = width; co->isMapped = 0; co->callback = NULL; co->destroyCallback = NULL; co->ops = &entryOps; en->flags = flags; en->cursorPosition = 0; en->firstChar = 0; en->bufUsed = 0; en->bufAlloced = width + 1; en->filter = NULL; if (!(en->flags & NEWT_FLAG_DISABLED)) co->takesFocus = 1; else co->takesFocus = 0; if (initialValue && strlen(initialValue) > (unsigned int)width) { en->bufAlloced = strlen(initialValue) + 1; } en->buf = malloc(en->bufAlloced); en->resultPtr = resultPtr; if (en->resultPtr) *en->resultPtr = en->buf; memset(en->buf, 0, en->bufAlloced); if (initialValue) { strcpy(en->buf, initialValue); en->bufUsed = strlen(initialValue); en->cursorPosition = en->bufUsed; /* move cursor back if entry is full */ if (en->cursorPosition && !(en->flags & NEWT_FLAG_SCROLL || wstrlen(en->buf, -1) < co->width)) en->cursorPosition = previous_char(en->buf, en->cursorPosition); } else { *en->buf = '\0'; en->bufUsed = 0; en->cursorPosition = 0; } en->cs = NEWT_COLORSET_ENTRY; en->csDisabled = NEWT_COLORSET_DISENTRY; return co; } static void scroll(struct entry *en, int width) { int r, lv, rv, cntx, cw, cn, nc, pc, ncw, pcw; if (width <= 1) { en->firstChar = en->cursorPosition; return; } cntx = width / 4; if (cntx > 5) cntx = 5; if (en->cursorPosition < en->firstChar) en->firstChar = en->cursorPosition; cn = next_char(en->buf, en->cursorPosition); cw = en->cursorPosition >= en->bufUsed ? 1 : wstrlen(en->buf + en->cursorPosition, cn - en->cursorPosition); r = wstrlen(en->buf + cn, -1); lv = wstrlen(en->buf + en->firstChar, en->cursorPosition - en->firstChar); rv = width - lv - cw; #define RC (ncw > 0 && (r > rv && lv - ncw >= cntx && rv < cntx)) #define LC (pcw > 0 && (r + pcw <= rv || (lv < cntx && rv - pcw >= cntx))) nc = next_char(en->buf, en->firstChar); ncw = wstrlen(en->buf + en->firstChar, nc - en->firstChar); if (RC) { do { lv -= ncw; rv += ncw; en->firstChar = nc; nc = next_char(en->buf, en->firstChar); ncw = wstrlen(en->buf + en->firstChar, nc - en->firstChar); } while (RC); return; } pc = previous_char(en->buf, en->firstChar); pcw = wstrlen(en->buf + pc, en->firstChar - pc); if (LC) { do { lv += pcw; rv -= pcw; en->firstChar = pc; pc = previous_char(en->buf, en->firstChar); pcw = wstrlen(en->buf + pc, en->firstChar - pc); } while (LC); } } static void entryDraw(newtComponent co) { struct entry * en = co->data; int i; char * chptr; int len; char *tmpptr = NULL; if (!co->isMapped) return; if (en->flags & NEWT_FLAG_DISABLED) SLsmg_set_color(en->csDisabled); else SLsmg_set_color(en->cs); if (en->flags & NEWT_FLAG_HIDDEN) { newtGotorc(co->top, co->left); for (i = 0; i < co->width; i++) SLsmg_write_char('_'); newtGotorc(co->top, co->left); return; } newtTrashScreen(); /* scroll if necessary */ scroll(en, co->width); chptr = en->buf + en->firstChar; if (en->flags & NEWT_FLAG_PASSWORD) { len = wstrlen(chptr, -1); tmpptr = alloca(len + 1); for (i = 0; i < len; i++) memset(tmpptr, '*', len); tmpptr[len] = '\0'; chptr = tmpptr; } len = wstrlen(chptr, -1); /* workaround for double width characters */ if (co->width > 1) { i = len < co->width ? len : co->width; i = i > 2 ? i - 2 : 0; newtGotorc(co->top, co->left + i); SLsmg_write_char('_'); SLsmg_write_char('_'); } newtGotorc(co->top, co->left); if (len <= co->width) { i = len; SLsmg_write_string(chptr); while (i < co->width) { SLsmg_write_char('_'); i++; } } else SLsmg_write_nstring(chptr, co->width); newtGotorc(co->top, co->left + wstrlen(en->buf+en->firstChar, en->cursorPosition - en->firstChar)); } void newtEntrySetFlags(newtComponent co, int flags, enum newtFlagsSense sense) { struct entry * en = co->data; int row, col; en->flags = newtSetFlags(en->flags, flags, sense); if (!(en->flags & NEWT_FLAG_DISABLED)) co->takesFocus = 1; else co->takesFocus = 0; newtGetrc(&row, &col); entryDraw(co); newtGotorc(row, col); } void newtEntrySetColors(newtComponent co, int normal, int disabled) { struct entry * en = co->data; en->cs = normal; en->csDisabled = disabled; entryDraw(co); } static void entryDestroy(newtComponent co) { struct entry * en = co->data; free(en->buf); free(en); free(co); } static struct eventResult entryEvent(newtComponent co, struct event ev) { struct entry * en = co->data; struct eventResult er; int ch; er.result = ER_IGNORED; if (ev.when == EV_NORMAL) { switch (ev.event) { case EV_FOCUS: newtCursorOn(); if (en->flags & NEWT_FLAG_HIDDEN) newtGotorc(co->top, co->left); else newtGotorc(co->top, co->left + wstrlen(en->buf + en->firstChar, en->cursorPosition - en->firstChar)); er.result = ER_SWALLOWED; break; case EV_UNFOCUS: newtCursorOff(); newtGotorc(0, 0); er.result = ER_SWALLOWED; if (co->callback) co->callback(co, co->callbackData); break; case EV_KEYPRESS: ch = ev.u.key; if (en->filter) ch = en->filter(co, en->filterData, ch, en->cursorPosition); if (ch) er = entryHandleKey(co, ch); break; case EV_MOUSE: if ((ev.u.mouse.type == MOUSE_BUTTON_DOWN) && (en->flags ^ NEWT_FLAG_HIDDEN)) { if (strlen(en->buf) >= ev.u.mouse.x - co->left) { en->cursorPosition = ev.u.mouse.x - co->left; newtGotorc(co->top, co->left +(en->cursorPosition - en->firstChar)); } else { en->cursorPosition = strlen(en->buf); newtGotorc(co->top, co->left +(en->cursorPosition - en->firstChar)); } } break; } } return er; } static int previous_char(const char *buf, int pos) { int len = 0; int off = 0; while (off < pos) { len = mblen(buf+off, MB_CUR_MAX); if (len <= 0) return pos; off+=len; } return off-len; } static int next_char(const char *buf, int pos) { int len = mblen(buf + pos, MB_CUR_MAX); if (len <= 0) return pos; return pos+len; } static struct eventResult entryHandleKey(newtComponent co, int key) { struct entry * en = co->data; struct eventResult er; char * chptr; er.result = ER_SWALLOWED; switch (key) { case '\r': /* Return */ if (en->flags & NEWT_FLAG_RETURNEXIT) { newtCursorOff(); er.result = ER_EXITFORM; } else { er.result = ER_NEXTCOMP; } break; case '\001': /* ^A */ case NEWT_KEY_HOME: en->cursorPosition = 0; break; case '\005': /* ^E */ case NEWT_KEY_END: en->cursorPosition = en->bufUsed; break; case '\013': /* ^K */ en->bufUsed = en->cursorPosition; memset(en->buf + en->bufUsed, 0, en->bufAlloced - en->bufUsed); break; case '\025': /* ^U */ en->bufUsed -= en->cursorPosition; memmove(en->buf, en->buf + en->cursorPosition, en->bufUsed); en->cursorPosition = 0; memset(en->buf + en->bufUsed, 0, en->bufAlloced - en->bufUsed); break; case '\002': /* ^B */ case NEWT_KEY_LEFT: if (en->cursorPosition) en->cursorPosition = previous_char(en->buf, en->cursorPosition); break; case '\004': case NEWT_KEY_DELETE: chptr = en->buf + en->cursorPosition; if (*chptr) { int delta = next_char(en->buf, en->cursorPosition)-en->cursorPosition; if (delta) { chptr+=delta; while (*chptr) { *(chptr - delta) = *chptr; chptr++; } memset(chptr - delta, 0, delta); en->bufUsed-=delta; } } break; case NEWT_KEY_BKSPC: { int prev = previous_char(en->buf, en->cursorPosition); if (en->cursorPosition != prev) { /* if this isn't true, there's nothing to erase */ int delta = en->cursorPosition - prev; chptr = en->buf + en->cursorPosition; en->bufUsed-=delta; en->cursorPosition-=delta; while (*chptr) { *(chptr - delta) = *chptr; chptr++; } memset(chptr - delta, 0, delta); } } break; case '\006': /* ^B */ case NEWT_KEY_RIGHT: if (en->cursorPosition < en->bufUsed) en->cursorPosition = next_char(en->buf, en->cursorPosition); break; default: if ((key >= 0x20 && key <= 0x7e) || (key >= 0x80 && key <= 0xff)) { char s[MB_CUR_MAX]; mbstate_t ps; int i, l; for (i = 1, s[0] = key; ; i++) { memset(&ps, 0, sizeof (ps)); l = mbrtowc(NULL, s, i, &ps); if (l == -1) /* invalid sequence */ i = 0; if (l != -2) /* not incomplete sequence */ break; /* read next byte */ if (i == MB_CUR_MAX || !SLang_input_pending(1)) { i = 0; break; } s[i] = SLang_getkey(); } if (!i || (!(en->flags & NEWT_FLAG_SCROLL) && wstrlen(en->buf, -1) + wstrlen(s, i) > co->width)) { /* FIXME this is broken */ SLtt_beep(); break; } if ((en->bufUsed + i) >= en->bufAlloced) { en->bufAlloced += 20; en->buf = realloc(en->buf, en->bufAlloced); if (en->resultPtr) *en->resultPtr = en->buf; memset(en->buf + en->bufAlloced - 20, 0, 20); } if (en->cursorPosition != en->bufUsed) { /* insert the new character */ memmove(en->buf + en->cursorPosition + i, en->buf + en->cursorPosition, en->bufUsed - en->cursorPosition); } en->bufUsed += i; for (l = 0; l < i; l++) en->buf[en->cursorPosition++] = s[l]; } else { er.result = ER_IGNORED; } } if (en->cursorPosition == en->bufUsed && en->cursorPosition && !(en->flags & NEWT_FLAG_SCROLL || wstrlen(en->buf, -1) < co->width)) en->cursorPosition = previous_char(en->buf, en->cursorPosition); entryDraw(co); return er; } char * newtEntryGetValue(newtComponent co) { struct entry * en = co->data; return en->buf; } void newtEntrySetFilter(newtComponent co, newtEntryFilter filter, void * data) { struct entry * en = co->data; en->filter = filter; en->filterData = data; } int newtEntryGetCursorPosition (newtComponent co) { struct entry * en = co->data; return en->cursorPosition; } void newtEntrySetCursorPosition (newtComponent co, int position) { struct entry * en = co->data; en->cursorPosition = position; }