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

listbox.c

/* This goofed-up box whacked into shape by Elliot Lee <sopwith@cuc.edu>
   (from the original listbox by Erik Troan <ewt@redhat.com>)
   and contributed to newt for use under the LGPL license.
   Copyright (C) 1996, 1997 Elliot Lee */

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

#include "newt.h"
#include "newt_pr.h"


/* Linked list of items in the listbox */
struct items {
    char * text;
    const void *data;
    unsigned char isSelected;
    struct items *next;
};

/* Holds all the relevant information for this listbox */
struct listbox {
    newtComponent sb;   /* Scrollbar on right side of listbox */
    int curWidth; /* size of text w/o scrollbar or border*/
    int curHeight;      /* size of text w/o border */
    int sbAdjust;
    int bdxAdjust, bdyAdjust;
    int numItems, numSelected;
    int userHasSetWidth;
    int currItem, startShowItem; /* startShowItem is the first item displayed
                           on the screen */
    int isActive; /* If we handle key events all the time, it seems
                 to do things even when they are supposed to be for
                 another button/whatever */
    struct items *boxItems;
    int grow;
    int flags; /* flags for this listbox, right now just
              NEWT_FLAG_RETURNEXIT */
};

static void listboxDraw(newtComponent co);
static void listboxDestroy(newtComponent co);
static struct eventResult listboxEvent(newtComponent co, struct event ev);
static void newtListboxRealSetCurrent(newtComponent co);
static void listboxPlace(newtComponent co, int newLeft, int newTop);
static inline void updateWidth(newtComponent co, struct listbox * li,
                        int maxField);
static void listboxMapped(newtComponent co, int isMapped);

static struct componentOps listboxOps = {
    listboxDraw,
    listboxEvent,
    listboxDestroy,
    listboxPlace,
    listboxMapped,
};

static void listboxMapped(newtComponent co, int isMapped) {
    struct listbox * li = co->data;

    co->isMapped = isMapped;
    if (li->sb)
      li->sb->ops->mapped(li->sb, isMapped);
}

static void listboxPlace(newtComponent co, int newLeft, int newTop) {
    struct listbox * li = co->data;

    co->top = newTop;
    co->left = newLeft;

    if (li->sb)
      li->sb->ops->place(li->sb, co->left + co->width - li->bdxAdjust - 1,
                     co->top + li->bdyAdjust);
}

newtComponent newtListbox(int left, int top, int height, int flags) {
    newtComponent co, sb;
    struct listbox * li;

    if (!(co = malloc(sizeof(*co))))
      return NULL;

    if (!(li = malloc(sizeof(struct listbox)))) {
      free(co);
      return NULL;
    }

    li->boxItems = NULL;
    li->numItems = 0;
    li->currItem = 0;
    li->numSelected = 0;
    li->isActive = 0;
    li->userHasSetWidth = 0;
    li->startShowItem = 0;
    li->sbAdjust = 0;
    li->bdxAdjust = 0;
    li->bdyAdjust = 0;
    li->flags = flags & (NEWT_FLAG_RETURNEXIT | NEWT_FLAG_BORDER |
                   NEWT_FLAG_MULTIPLE | NEWT_FLAG_SHOWCURSOR);

    if (li->flags & NEWT_FLAG_BORDER) {
      li->bdxAdjust = 2;
      li->bdyAdjust = 1;
    }

    co->height = height;
    li->curHeight = co->height - (2 * li->bdyAdjust);

    if (height) {
      li->grow = 0;
      if (flags & NEWT_FLAG_SCROLL) {
          sb = newtVerticalScrollbar(left, top + li->bdyAdjust,
                              li->curHeight,
                              COLORSET_LISTBOX, COLORSET_ACTLISTBOX);
          li->sbAdjust = 3;
      } else {
          sb = NULL;
      }
    } else {
      li->grow = 1;
      sb = NULL;
    }

    li->sb = sb;
    co->data = li;
    co->isMapped = 0;
    co->left = left;
    co->top = top;
    co->ops = &listboxOps;
    co->takesFocus = 1;
    co->callback = NULL;
    co->destroyCallback = NULL;

    updateWidth(co, li, 5);

    return co;
}

static inline void updateWidth(newtComponent co, struct listbox * li,
                        int maxField) {
    li->curWidth = maxField;
    co->width = li->curWidth + li->sbAdjust + 2 * li->bdxAdjust;

    if (li->sb)
      li->sb->left = co->left + co->width - li->bdxAdjust - 1;
}

void newtListboxSetCurrentByKey(newtComponent co, void * key) {
    struct listbox * li = co->data;
    struct items * item;
    int i;

    item = li->boxItems, i = 0;
    while (item && item->data != key)
      item = item->next, i++;

    if (item)
      newtListboxSetCurrent(co, i);
}

void newtListboxSetCurrent(newtComponent co, int num)
{
    struct listbox * li = co->data;

    if (num >= li->numItems)
      li->currItem = li->numItems - 1;
    else if (num < 0)
      li->currItem = 0;
    else
      li->currItem = num;

    if (li->currItem < li->startShowItem)
      li->startShowItem = li->currItem;
    else if (li->currItem - li->startShowItem > li->curHeight - 1)
      li->startShowItem = li->currItem - li->curHeight + 1;
    if (li->startShowItem + li->curHeight > li->numItems)
      li->startShowItem = li->numItems - li->curHeight;
    if(li->startShowItem < 0)
      li->startShowItem = 0;

    newtListboxRealSetCurrent(co);
}

static void newtListboxRealSetCurrent(newtComponent co)
{
    struct listbox * li = co->data;

    if(li->sb)
      newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
    listboxDraw(co);
    if(co->callback) co->callback(co, co->callbackData);
}

void newtListboxSetWidth(newtComponent co, int width) {
    struct listbox * li = co->data;

    co->width = width;
    li->curWidth = co->width - li->sbAdjust - 2 * li->bdxAdjust;
    li->userHasSetWidth = 1;
    if (li->sb)
      li->sb->left = co->left + co->width - li->bdxAdjust - 1;
    listboxDraw(co);
}

void * newtListboxGetCurrent(newtComponent co) {
    struct listbox * li = co->data;
    int i;
    struct items *item;

    for(i = 0, item = li->boxItems; item != NULL && i < li->currItem;
      i++, item = item->next);

    if (item)
      return (void *)item->data;
    else
      return NULL;
}

void newtListboxSelectItem(newtComponent co, const void * key,
      enum newtFlagsSense sense)
{
    struct listbox * li = co->data;
    int i;
    struct items * item;

    item = li->boxItems, i = 0;
    while (item && item->data != key)
      item = item->next, i++;

    if (!item) return;

    if (item->isSelected)
      li->numSelected--;

    switch(sense) {
      case NEWT_FLAGS_RESET:
            item->isSelected = 0; break;
      case NEWT_FLAGS_SET:
            item->isSelected = 1; break;
      case NEWT_FLAGS_TOGGLE:
            item->isSelected = !item->isSelected;
    }

    if (item->isSelected)
      li->numSelected++;

    listboxDraw(co);
}

void newtListboxClearSelection(newtComponent co)
{
    struct items *item;
    struct listbox * li = co->data;

    for(item = li->boxItems; item != NULL;
      item = item->next)
      item->isSelected = 0;
    li->numSelected = 0;
    listboxDraw(co);
}

/* Free the returned array after use, but NOT the values in the array */
void ** newtListboxGetSelection(newtComponent co, int *numitems)
{
    struct listbox * li;
    int i;
    void **retval;
    struct items *item;

    if(!co || !numitems) return NULL;

    li = co->data;
    if(!li || !li->numSelected) return NULL;

    retval = malloc(li->numSelected * sizeof(void *));
    for(i = 0, item = li->boxItems; item != NULL;
      item = item->next)
      if(item->isSelected)
          retval[i++] = (void *)item->data;
    *numitems = li->numSelected;
    return retval;
}

void newtListboxSetEntry(newtComponent co, int num, const char * text) {
    struct listbox * li = co->data;
    int i;
    struct items *item;

    for(i = 0, item = li->boxItems; item != NULL && i < num;
      i++, item = item->next);

    if(!item)
      return;
    else {
      free(item->text);
      item->text = strdup(text);
    }
    if (li->userHasSetWidth == 0 && wstrlen(text,-1) > li->curWidth) {
      updateWidth(co, li, wstrlen(text,-1));
    }

    if (num >= li->startShowItem && num <= li->startShowItem + co->height)
      listboxDraw(co);
}

void newtListboxSetData(newtComponent co, int num, void * data) {
    struct listbox * li = co->data;
    int i;
    struct items *item;

    for(i = 0, item = li->boxItems; item != NULL && i < num;
      i++, item = item->next);

    item->data = data;
}

int newtListboxAppendEntry(newtComponent co, const char * text,
                      const void * data) {
    struct listbox * li = co->data;
    struct items *item;

    if(li->boxItems) {
      for (item = li->boxItems; item->next != NULL; item = item->next);

      item = item->next = malloc(sizeof(struct items));
    } else {
      item = li->boxItems = malloc(sizeof(struct items));
    }

    if (!li->userHasSetWidth && text && (wstrlen(text,-1) > li->curWidth))
      updateWidth(co, li, wstrlen(text,-1));

    item->text = strdup(text); item->data = data; item->next = NULL;
    item->isSelected = 0;

    if (li->grow)
      co->height++, li->curHeight++;
    li->numItems++;

    return 0;
}

int newtListboxInsertEntry(newtComponent co, const char * text,
                         const void * data, void * key) {
    struct listbox * li = co->data;
    struct items *item, *t;

    if (li->boxItems) {
      if (key) {
          item = li->boxItems;
          while (item && item->data != key) item = item->next;

          if (!item) return 1;

          t = item->next;
          item = item->next = malloc(sizeof(struct items));
          item->next = t;
      } else {
          t = li->boxItems;
          item = li->boxItems = malloc(sizeof(struct items));
          item->next = t;
      }
    } else if (key) {
      return 1;
    } else {
      item = li->boxItems = malloc(sizeof(struct items));
      item->next = NULL;
    }

    if (!li->userHasSetWidth && text && (wstrlen(text,-1) > li->curWidth))
      updateWidth(co, li, wstrlen(text,-1));

    item->text = strdup(text?text:"(null)"); item->data = data;
    item->isSelected = 0;

    if (li->sb)
      li->sb->left = co->left + co->width - li->bdxAdjust - 1;
    li->numItems++;

    listboxDraw(co);

    return 0;
}

int newtListboxDeleteEntry(newtComponent co, void * key) {
    struct listbox * li = co->data;
    int widest = 0, t;
    struct items *item, *item2 = NULL;
    int num;

    if (li->boxItems == NULL || li->numItems <= 0)
      return 0;

    num = 0;

    item2 = NULL, item = li->boxItems;
    while (item && item->data != key) {
      item2 = item;
      item = item->next;
      num++;
    }

    if (!item)
      return -1;

    if (item2)
      item2->next = item->next;
    else
      li->boxItems = item->next;

    free(item->text);
    free(item);
    li->numItems--;

    if (!li->userHasSetWidth) {
      widest = 0;
      for (item = li->boxItems; item != NULL; item = item->next)
          if ((t = wstrlen(item->text,-1)) > widest) widest = t;
    }

    if (li->currItem >= num)
      li->currItem--;

    if (!li->userHasSetWidth) {
      updateWidth(co, li, widest);
    }

    listboxDraw(co);

    return 0;
}

void newtListboxClear(newtComponent co)
{
    struct listbox * li;
    struct items *anitem, *nextitem;
    if(co == NULL || (li = co->data) == NULL)
      return;
    for(anitem = li->boxItems; anitem != NULL; anitem = nextitem) {
      nextitem = anitem->next;
      free(anitem->text);
      free(anitem);
    }
    li->numItems = li->numSelected = li->currItem = li->startShowItem = 0;
    li->boxItems = NULL;
    if (!li->userHasSetWidth)
      updateWidth(co, li, 5);
}

int newtListboxItemCount(newtComponent co)
{
    struct listbox *li = co->data;
    return li->numItems;
}

/* If you don't want to get back the text, pass in NULL for the ptr-ptr. Same
   goes for the data. */
void newtListboxGetEntry(newtComponent co, int num, char **text, void **data) {
    struct listbox * li = co->data;
    int i;
    struct items *item;

    if (!li->boxItems || num >= li->numItems) {
      if(text)
          *text = NULL;
      if(data)
          *data = NULL;
      return;
    }

    i = 0;
    item = li->boxItems;
    while (item && i < num) {
      i++, item = item->next;
    }

    if (item) {
      if (text)
          *text = item->text;
      if (data)
          *data = (void *)item->data;
    }
}

static void listboxDraw(newtComponent co)
{
    struct listbox * li = co->data;
    struct items *item;
    int i, j;

    if (!co->isMapped) return ;

    newtTrashScreen();
    
    if(li->flags & NEWT_FLAG_BORDER) {
      if(li->isActive)
        SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX);
      else
          SLsmg_set_color(NEWT_COLORSET_LISTBOX);

      newtDrawBox(co->left, co->top, co->width, co->height, 0);
    }

    if(li->sb)
      li->sb->ops->draw(li->sb);

    SLsmg_set_color(NEWT_COLORSET_LISTBOX);

    for(i = 0, item = li->boxItems; item != NULL && i < li->startShowItem;
      i++, item = item->next);

    j = i;

    for (i = 0; item != NULL && i < li->curHeight; i++, item = item->next) {
      if (!item->text) continue;

      newtGotorc(co->top + i + li->bdyAdjust, co->left + li->bdxAdjust);
      if(j + i == li->currItem) {
          if(li->isActive)
            SLsmg_set_color(NEWT_COLORSET_ACTSELLISTBOX);
          else
            SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX);
      } else if(item->isSelected)
          SLsmg_set_color(NEWT_COLORSET_SELLISTBOX);
      else
          SLsmg_set_color(NEWT_COLORSET_LISTBOX);

      SLsmg_write_nstring(item->text, li->curWidth);

      if (li->flags & NEWT_FLAG_MULTIPLE) {
          newtGotorc(co->top + i + li->bdyAdjust, co->left + li->bdxAdjust);
          SLsmg_set_color(item->isSelected ?
                NEWT_COLORSET_SELLISTBOX : NEWT_COLORSET_LISTBOX);
          SLsmg_write_nstring(item->text, 1);
      }
    }
    newtGotorc(co->top + (li->currItem - li->startShowItem) + li->bdyAdjust,
               co->left + li->bdxAdjust);
}

static struct eventResult listboxEvent(newtComponent co, struct event ev) {
    struct eventResult er;
    struct listbox * li = co->data;
    struct items *item;
    int i;
    
    er.result = ER_IGNORED;

    if(ev.when == EV_EARLY || ev.when == EV_LATE) {
      return er;
    }

    switch(ev.event) {
      case EV_KEYPRESS:
      if (!li->isActive) break;

      switch(ev.u.key) {
        case ' ':
          if(!(li->flags & NEWT_FLAG_MULTIPLE)) break;
          newtListboxSelectItem(co, newtListboxGetCurrent(co),
                          NEWT_FLAGS_TOGGLE);
          er.result = ER_SWALLOWED;
          /* We don't break here, because it is cool to be able to
             hold space to select a bunch of items in a list at once */

        case NEWT_KEY_DOWN:
          if(li->numItems <= 0) break;
          if(li->currItem < li->numItems - 1) {
            li->currItem++;
            if(li->currItem > (li->startShowItem + li->curHeight - 1)) {
                li->startShowItem = li->currItem - li->curHeight + 1;
                if(li->startShowItem + li->curHeight > li->numItems)
                  li->startShowItem = li->numItems - li->curHeight;
            }
            if(li->sb)
                newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
            listboxDraw(co);
          }
          if(co->callback) co->callback(co, co->callbackData);
          er.result = ER_SWALLOWED;
          break;

        case NEWT_KEY_ENTER:
          if(li->numItems <= 0) break;
          if(li->flags & NEWT_FLAG_RETURNEXIT)
            er.result = ER_EXITFORM;
          break;

        case NEWT_KEY_UP:
          if(li->numItems <= 0) break;
          if(li->currItem > 0) {
            li->currItem--;
            if(li->currItem < li->startShowItem)
                li->startShowItem = li->currItem;
            if(li->sb)
                newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
            listboxDraw(co);
          }
          if(co->callback) co->callback(co, co->callbackData);
          er.result = ER_SWALLOWED;
          break;

        case NEWT_KEY_PGUP:
          if(li->numItems <= 0) break;
          li->startShowItem -= li->curHeight - 1;
          if(li->startShowItem < 0)
            li->startShowItem = 0;
          li->currItem -= li->curHeight - 1;
          if(li->currItem < 0)
            li->currItem = 0;
          newtListboxRealSetCurrent(co);
          er.result = ER_SWALLOWED;
          break;

        case NEWT_KEY_PGDN:
          if(li->numItems <= 0) break;
          li->startShowItem += li->curHeight;
          if(li->startShowItem > (li->numItems - li->curHeight)) {
            li->startShowItem = li->numItems - li->curHeight;
          }
          li->currItem += li->curHeight;
          if(li->currItem >= li->numItems) {
            li->currItem = li->numItems - 1;
          }
          newtListboxRealSetCurrent(co);
          er.result = ER_SWALLOWED;
          break;

        case NEWT_KEY_HOME:
          if(li->numItems <= 0) break;
          newtListboxSetCurrent(co, 0);
          er.result = ER_SWALLOWED;
          break;

        case NEWT_KEY_END:
          if(li->numItems <= 0) break;
          li->startShowItem = li->numItems - li->curHeight;
          if(li->startShowItem < 0)
            li->startShowItem = 0;
          li->currItem = li->numItems - 1;
          newtListboxRealSetCurrent(co);
          er.result = ER_SWALLOWED;
          break;
        default:
            if (li->numItems <= 0) break;
              if (ev.u.key < NEWT_KEY_EXTRA_BASE && isalpha(ev.u.key)) {
              for(i = 0, item = li->boxItems; item != NULL &&
                    i < li->currItem; i++, item = item->next);

              if (item && item->text && (toupper(*item->text) == toupper(ev.u.key))) {
                  item = item->next;
                  i++;
              } else { 
                  item = li->boxItems;
                  i = 0;
              }
              while (item && item->text &&
                   toupper(*item->text) != toupper(ev.u.key)) {
                  item = item->next;
                  i++;
              }
              if (item) {
                  li->currItem = i;
                  if(li->currItem < li->startShowItem ||
                   li->currItem > li->startShowItem)
                    li->startShowItem =
                        li->currItem > li->numItems - li->curHeight ?
                        li->startShowItem = li->numItems - li->curHeight :
                        li->currItem;
                  if(li->sb)
                    newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
                  newtListboxRealSetCurrent(co);
                  er.result = ER_SWALLOWED;
              }
            }
      }
      break;

      case EV_FOCUS:
      li->isActive = 1;
      listboxDraw(co);
      if(li->flags & NEWT_FLAG_SHOWCURSOR)
        newtCursorOn();
      er.result = ER_SWALLOWED;
      break;

      case EV_UNFOCUS:
      li->isActive = 0;
      listboxDraw(co);
      if(li->flags & NEWT_FLAG_SHOWCURSOR)
        newtCursorOff();
      er.result = ER_SWALLOWED;
      break;

      case EV_MOUSE:
        /* if this mouse click was within the listbox, make the current
           item the item clicked on. */
      /* Up scroll arrow */
      if (li->sb &&
          ev.u.mouse.x == co->left + co->width - li->bdxAdjust - 1 &&
          ev.u.mouse.y == co->top + li->bdyAdjust) {
          if(li->numItems <= 0) break;
          if(li->currItem > 0) {
            li->currItem--;
            if(li->currItem < li->startShowItem)
                li->startShowItem = li->currItem;
            if(li->sb)
                newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
            listboxDraw(co);
          }
          if(co->callback) co->callback(co, co->callbackData);
          er.result = ER_SWALLOWED;
          break;
      }
      /* Down scroll arrow */
      if (li->sb &&
          ev.u.mouse.x == co->left + co->width - li->bdxAdjust - 1 &&
          ev.u.mouse.y == co->top + co->height - li->bdyAdjust - 1) {
          if(li->numItems <= 0) break;
          if(li->currItem < li->numItems - 1) {
            li->currItem++;
            if(li->currItem > (li->startShowItem + li->curHeight - 1)) {
                li->startShowItem = li->currItem - li->curHeight + 1;
                if(li->startShowItem + li->curHeight > li->numItems)
                  li->startShowItem = li->numItems - li->curHeight;
            }
            if(li->sb)
                newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
            listboxDraw(co);
          }
          if(co->callback) co->callback(co, co->callbackData);
          er.result = ER_SWALLOWED;
          break;
      }
      if ((ev.u.mouse.y >= co->top + li->bdyAdjust) &&
          (ev.u.mouse.y <= co->top + co->height - (li->bdyAdjust * 2)) &&
          (ev.u.mouse.x >= co->left + li->bdxAdjust) &&
          (ev.u.mouse.x <= co->left + co->width + (li->bdxAdjust * 2))) {
          li->currItem = li->startShowItem +
            (ev.u.mouse.y - li->bdyAdjust - co->top);
          newtListboxRealSetCurrent(co);
          listboxDraw(co);
          if(co->callback) co->callback(co, co->callbackData);
          er.result = ER_SWALLOWED;
          break;
      }
    }

    return er;
}

static void listboxDestroy(newtComponent co) {
    struct listbox * li = co->data;
    struct items * item, * nextitem;

    nextitem = item = li->boxItems;

    while (item != NULL) {
      nextitem = item->next;
      free(item->text);
      free(item);
      item = nextitem;
    }

    if (li->sb) li->sb->ops->destroy(li->sb);

    free(li);
    free(co);
}

Generated by  Doxygen 1.6.0   Back to index