Logo Search packages:      
Sourcecode: newt version File versions  Download package

entry.c

#include "config.h"

#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif

#include <ctype.h>
#include <slang.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>

#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;
};

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;
    }

    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(NEWT_COLORSET_DISENTRY);
    else
      SLsmg_set_color(NEWT_COLORSET_ENTRY);

    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);
}

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;
}

Generated by  Doxygen 1.6.0   Back to index