Logo Search packages:      
Sourcecode: eagle-usb version File versions  Download package

inputstr.c

/*
 * $Id: inputstr.c,v 1.1 2004/02/15 13:21:43 Tux Exp $
 *
 *  inputstr.c -- functions for input/display of a string
 *
 *  AUTHOR: Thomas Dickey
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "dialog.h"

typedef struct _cache {
    struct _cache *next;
#ifdef HAVE_WGET_WCH
    struct _cache *cache_at;  /* unique: associate caches by CACHE */
    const char *string_at;    /* unique: associate caches by char* */
#endif
    unsigned s_len;           /* strlen(string) - we add 1 for EOS */
    unsigned i_len;           /* length(list) - we add 1 for EOS */
    char *string;       /* a copy of the last-processed string */
    int *list;                /* indices into the string */
} CACHE;

#ifdef HAVE_WGET_WCH
#define SAME_CACHE(c,s,l) (c->string != 0 && memcmp(c->string,s,l) == 0)

static CACHE *cache_list;

static void
make_cache(CACHE * cache, const char *string)
{
    CACHE *p;

    p = (CACHE *) calloc(1, sizeof(CACHE));
    assert_ptr(p, "load_cache");
    p->next = cache_list;
    cache_list = p;

    p->cache_at = cache;
    p->string_at = string;

    *cache = *p;
}

static void
load_cache(CACHE * cache, const char *string)
{
    CACHE *p;

    for (p = cache_list; p != 0; p = p->next) {
      if (p->cache_at == cache
          && p->string_at == string) {
          *cache = *p;
          return;
      }
    }
    make_cache(cache, string);
}

static void
save_cache(CACHE * cache, const char *string)
{
    CACHE *p;

    for (p = cache_list; p != 0; p = p->next) {
      if (p->cache_at == cache
          && p->string_at == string) {
          CACHE *q = p->next;
          *p = *cache;
          p->next = q;
          return;
      }
    }
}
#else
#define SAME_CACHE(c,s,l) (c->string != 0)
#define load_cache(cache, string)   /* nothing */
#define save_cache(cache, string)   /* nothing */
#endif

/*
 * If the given string has not changed, we do not need to update the index.
 * If we need to update the index, allocate enough memory for it.
 */
static bool
same_cache2(CACHE * cache, const char *string, unsigned i_len)
{
    unsigned need;
    unsigned s_len = strlen(string);

    if (cache->s_len != 0
      && cache->s_len >= s_len
      && cache->list != 0
      && SAME_CACHE(cache, string, s_len)) {
      return TRUE;
    }

    need = sizeof(int *) * (i_len + 1);
    if (cache->list == 0) {
      cache->list = malloc(need);
    } else if (cache->i_len < i_len) {
      cache->list = realloc(cache->list, need);
    }
    cache->i_len = i_len;

    if (cache->s_len >= s_len && cache->string != 0) {
      strcpy(cache->string, string);
    } else {
      if (cache->string != 0)
          free(cache->string);
      cache->string = strclone(string);
    }
    cache->s_len = s_len;

    return FALSE;
}

#ifdef HAVE_WGET_WCH
/*
 * Like same_cache2(), but we are only concerned about caching a copy of the
 * string and its associated length.
 */
static bool
same_cache1(CACHE * cache, const char *string, unsigned i_len)
{
    unsigned s_len = strlen(string);

    if (cache->s_len == s_len
      && SAME_CACHE(cache, string, s_len)) {
      return TRUE;
    }

    if (cache->s_len >= s_len && cache->string != 0) {
      strcpy(cache->string, string);
    } else {
      if (cache->string != 0)
          free(cache->string);
      cache->string = strclone(string);
    }
    cache->s_len = s_len;
    cache->i_len = i_len;

    return FALSE;
}
#endif

/*
 * Counts the number of bytes that make up complete wide-characters, up to byte
 * 'len'.
 */
#ifdef HAVE_WGET_WCH
static int
dlg_count_wcbytes(const char *string, size_t len)
{
    static CACHE cache;

    load_cache(&cache, string);
    if (!same_cache1(&cache, string, len)) {
      while (len != 0) {
          int part = 0;
          int result = 0;
          const char *src = cache.string;
          mbstate_t state;
          int save = cache.string[len];

          cache.string[len] = '\0';
          memset(&state, 0, sizeof(state));
          result = mbsrtowcs((wchar_t *) 0, &src, len, &state);
          cache.string[len] = save;
          if (result >= 0) {
            break;
          }
          ++part;
          --len;
      }
      cache.i_len = len;
    }
    save_cache(&cache, string);
    return cache.i_len;
}
#else
#define dlg_count_wcbytes(string,len) len
#endif

/*
 * Counts the number of wide-characters in the string.
 */
int
dlg_count_wchars(const char *string)
{
#ifdef HAVE_WGET_WCH
    static CACHE cache;
    size_t len = strlen(string);

    load_cache(&cache, string);
    if (!same_cache1(&cache, string, len)) {
      const char *src = cache.string;
      mbstate_t state;
      int part = dlg_count_wcbytes(cache.string, len);
      int save = cache.string[part];
      int result;
      wchar_t *temp = calloc(sizeof(wchar_t), len + 1);

      cache.string[part] = '\0';
      memset(&state, 0, sizeof(state));
      result = mbsrtowcs(temp, &src, part, &state);
      cache.string[part] = save;
      cache.i_len = wcslen(temp);
      free(temp);
    }

    save_cache(&cache, string);
    return cache.i_len;
#else
    return strlen(string);
#endif
}

/*
 * Build an index of the wide-characters in the string, so we can easily tell
 * which byte-offset begins a given wide-character.
 */
const int *
dlg_index_wchars(const char *string)
{
    static CACHE cache;
    unsigned len = dlg_count_wchars(string);
    unsigned inx;

    load_cache(&cache, string);
    if (!same_cache2(&cache, string, len)) {
      const char *current = string;

      cache.list[0] = 0;
      for (inx = 1; inx <= len; ++inx) {
#ifdef HAVE_WGET_WCH
          mbstate_t state;
          int width;
          memset(&state, 0, sizeof(state));
          width = mbrlen(current, strlen(current), &state);
          if (width <= 0)
            width = 1;  /* FIXME: what if we have a control-char? */
          current += width;
          cache.list[inx] = cache.list[inx - 1] + width;
#else
          (void) current;
          cache.list[inx] = inx;
#endif
      }
    }
    save_cache(&cache, string);
    return cache.list;
}

/*
 * Given the character-offset to find in the list, return the corresponding
 * array index.
 */
static int
dlg_find_index(const int *list, int limit, int to_find)
{
    int result;
    for (result = 0; result <= limit; ++result) {
      if (to_find == list[result]
          || result == limit
          || to_find < list[result + 1])
          break;
    }
    return result;
}

/*
 * Build a list of the display-columns for the given string's characters.
 */
const int *
dlg_index_columns(const char *string)
{
    static CACHE cache;
    unsigned len = dlg_count_wchars(string);
    unsigned inx;

    load_cache(&cache, string);
    if (!same_cache2(&cache, string, len)) {
      cache.list[0] = 0;
#ifdef HAVE_WGET_WCH
      {
          size_t num_bytes = strlen(string);
          const int *inx_wchars = dlg_index_wchars(string);
          mbstate_t state;

          for (inx = 0; inx < len; ++inx) {
            wchar_t temp;
            int check;
            int result;

            memset(&state, 0, sizeof(state));
            check = mbrtowc(&temp, string + inx_wchars[inx], num_bytes -
                        inx_wchars[inx], &state);
            if (check <= 0)
                result = 1;
            else
                result = wcwidth(temp);
            if (result < 0) {
                cchar_t temp2;
                setcchar(&temp2, &temp, 0, 0, 0);
                result = wcslen(wunctrl(&temp2));
            }
            cache.list[inx + 1] = result;
            if (inx > 0)
                cache.list[inx + 1] += cache.list[inx];
          }
      }
#else
      for (inx = 0; inx < len; ++inx) {
          cache.list[inx + 1] = (isprint(UCH(string[inx]))
                           ? 1
                           : strlen(unctrl(string[inx])));
          if (inx > 0)
            cache.list[inx + 1] += cache.list[inx];
      }
#endif
    }
    save_cache(&cache, string);
    return cache.list;
}

/*
 * Returns the number of columns used for a string.  That happens to be the
 * end-value of the cols[] array.
 */
int
dlg_count_columns(const char *string)
{
    int result = 0;
    int limit = dlg_count_wchars(string);
    if (limit > 0) {
      const int *cols = dlg_index_columns(string);
      result = cols[limit];
    }
    return strlen(string);
}

/*
 * Given a column limit, count the number of wide characters that can fit
 * into that limit.  The offset is used to skip over a leading character
 * that was already written.
 */
int
dlg_limit_columns(const char *string, int limit, int offset)
{
    const int *cols = dlg_index_columns(string);
    int result = dlg_count_wchars(string);

    while (result > 0 && (cols[result] - cols[offset]) > limit)
      --result;
    return result;
}

/*
 * Updates the string and character-offset, given various editing characters
 * or literal characters which are inserted at the character-offset.
 */
bool
dlg_edit_string(char *string, int *chr_offset, int key, int fkey, bool force)
{
    int i;
    int len = strlen(string);
    int limit = dlg_count_wchars(string);
    const int *indx = dlg_index_wchars(string);
    int offset = dlg_find_index(indx, limit, *chr_offset);
    int max_len = MAX_LEN;
    bool edit = TRUE;

    if (dialog_vars.max_input != 0 && dialog_vars.max_input < MAX_LEN)
      max_len = dialog_vars.max_input;

    /* transform editing characters into equivalent function-keys */
    if (!fkey) {
      fkey = TRUE;            /* assume we transform */
      switch (key) {
      case 0:
          break;
      case CHR_BACKSPACE:
          key = KEY_BACKSPACE;
          break;
      case 21:          /* ^U */
          key = KEY_DL;
          break;
      case CHR_DELETE:
          key = KEY_DC;
          break;
      case '\n':
      case '\r':
          key = KEY_ENTER;
          break;
      case ESC:
      case TAB:
          fkey = FALSE; /* this is used for navigation */
          break;
      default:
          fkey = FALSE; /* ...no, we did not transform */
          break;
      }
    }

    if (fkey) {
      switch (key) {
      case 0:           /* special case for loop entry */
          edit = force;
          break;
      case KEY_LEFT:
          if (*chr_offset)
            *chr_offset = indx[offset - 1];
          break;
      case KEY_RIGHT:
          if (offset < limit)
            *chr_offset = indx[offset + 1];
          break;
      case KEY_HOME:
          if (*chr_offset)
            *chr_offset = 0;
          break;
      case KEY_END:
          if (offset < limit)
            *chr_offset = indx[limit];
          break;
      case KEY_BACKSPACE:
          if (offset) {
            int gap = indx[offset] - indx[offset - 1];
            *chr_offset = indx[offset - 1];
            if (gap > 0) {
                for (i = *chr_offset;
                   (string[i] = string[i + gap]) != '\0';
                   i++) {
                  ;
                }
            }
          }
          break;
      case KEY_DC:
          if (limit) {
            if (--limit == 0) {
                string[*chr_offset = 0] = '\0';
            } else {
                int gap = ((offset <= limit)
                         ? (indx[offset + 1] - indx[offset])
                         : 0);
                if (gap > 0) {
                  for (i = indx[offset];
                       (string[i] = string[i + gap]) != '\0';
                       i++) {
                      ;
                  }
                } else if (offset > 0) {
                  string[indx[offset - 1]] = '\0';
                }
                if (*chr_offset > indx[limit])
                  *chr_offset = indx[limit];
            }
          }
          break;
      case KEY_DL:
          string[*chr_offset = 0] = '\0';
          break;
      case KEY_ENTER:
          edit = 0;
          break;
      default:
          beep();
          break;
      }
    } else {
      if (key == ESC || key == TAB) {
          edit = 0;
      } else {
          if (len < max_len) {
            for (i = ++len; i > *chr_offset; i--)
                string[i] = string[i - 1];
            string[*chr_offset] = key;
            *chr_offset += 1;
          } else {
            (void) beep();
          }
      }
    }
    return edit;
}

static void
compute_edit_offset(const char *string,
                int chr_offset,
                int x_last,
                int *p_dpy_column,
                int *p_scroll_amt)
{
    const int *cols = dlg_index_columns(string);
    const int *indx = dlg_index_wchars(string);
    int limit = dlg_count_wchars(string);
    int offset = dlg_find_index(indx, limit, chr_offset);
    int offset2;
    int dpy_column;
    int n;

    for (n = offset2 = 0; n <= offset; ++n) {
      if ((cols[offset] - cols[n]) < x_last
          && (offset == limit || (cols[offset + 1] - cols[n]) < x_last)) {
          offset2 = n;
          break;
      }
    }

    dpy_column = cols[offset] - cols[offset2];

    if (p_dpy_column != 0)
      *p_dpy_column = dpy_column;
    if (p_scroll_amt != 0)
      *p_scroll_amt = offset2;
}

/*
 * Given the character-offset in the string, returns the display-offset where
 * we will position the cursor.
 */
int
dlg_edit_offset(char *string, int chr_offset, int x_last)
{
    int result;

    compute_edit_offset(string, chr_offset, x_last, &result, 0);

    return result;
}

/*
 * Displays the string, shifted as necessary, to fit within the box and show
 * the current character-offset.
 */
void
dlg_show_string(WINDOW *win,
            const char *string,     /* string to display (may be multibyte) */
            int chr_offset,   /* character (not bytes) offset */
            chtype attr,      /* window-attributes */
            int y_base, /* beginning row on screen */
            int x_base, /* beginning column on screen */
            int x_last, /* number of columns on screen */
            bool hidden,      /* if true, do not echo */
            bool force) /* if true, force repaint */
{
    x_last = MIN(x_last + x_base, getmaxx(win)) - x_base;

    if (hidden) {
      if (force) {
          (void) wmove(win, y_base, x_base);
          wrefresh(win);
      }
    } else {
      const int *cols = dlg_index_columns(string);
      const int *indx = dlg_index_wchars(string);
      int limit = dlg_count_wchars(string);

      int i, j, k;
      int input_x;
      int scrollamt;

      compute_edit_offset(string, chr_offset, x_last, &input_x, &scrollamt);

      wattrset(win, attr);
      (void) wmove(win, y_base, x_base);
      for (i = scrollamt, k = 0; i < limit && k < x_last; ++i) {
          int check = cols[i + 1] - cols[scrollamt];
          if (check <= x_last) {
            for (j = indx[i]; j < indx[i + 1]; ++j) {
                waddch(win, CharOf(string[j]));
            }
            k = check;
          } else {
            break;
          }
      }
      while (k++ < x_last)
          waddch(win, ' ');
      (void) wmove(win, y_base, x_base + input_x);
      wrefresh(win);
    }
}

Generated by  Doxygen 1.6.0   Back to index