X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=listbox.c;h=aae4f836f7c108b02ca93de826fe3a1800637c85;hb=HEAD;hp=fb3a860bb71f983ff10d61e17c8b5972dc05beca;hpb=7ea3dccd74b5fdcef5425d16ace7635ac5fa8b0c;p=thirdparty%2Fnewt.git diff --git a/listbox.c b/listbox.c index fb3a860..aae4f83 100644 --- a/listbox.c +++ b/listbox.c @@ -1,150 +1,774 @@ -#include +/* This goofed-up box whacked into shape by Elliot Lee + (from the original listbox by Erik Troan ) + and contributed to newt for use under the LGPL license. + Copyright (C) 1996, 1997 Elliot Lee */ + +#include +#include #include #include +#include #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 * items, form; - int numItems; - int allocedItems; - int flags; - newtComponent sb; + 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; - co = malloc(sizeof(*co)); - li = malloc(sizeof(struct listbox)); + if (!(co = malloc(sizeof(*co)))) + return NULL; - li->allocedItems = 5; - li->numItems = 0; - li->flags = flags; - li->items = malloc(li->allocedItems * sizeof(*li->items)); + if (!(li = malloc(sizeof(struct listbox)))) { + free(co); + return NULL; + } - if (height) - sb = newtVerticalScrollbar(left, top, height, COLORSET_LISTBOX, - COLORSET_ACTLISTBOX); - else - sb = NULL; - li->form = newtForm(sb); - li->sb = sb; + 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) { - newtFormSetHeight(li->form, height); - newtFormAddComponent(li->form, sb); + 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->height = li->form->height; - co->width = li->form->width; co->ops = &listboxOps; co->takesFocus = 1; + co->callback = NULL; + co->destroyCallback = NULL; + + updateWidth(co, li, 5); return co; } -void newtListboxSetCurrent(newtComponent co, int num) { +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++; - newtFormSetCurrent(li->form, li->items[num]); + 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; - newtComponent curr; int i; + struct items *item; - /* Having to do this linearly is really, really dumb */ + for(i = 0, item = li->boxItems; item != NULL && i < li->currItem; + i++, item = item->next); - curr = newtFormGetCurrent(li->form); - for (i = 0; i < li->numItems; i++) { - if (li->items[i] == curr) break; - } - - if (li->items[i] == curr) - return newtListitemGetData(li->items[i]); + if (item) + return (void *)item->data; else return NULL; } -void newtListboxSetEntry(newtComponent co, int num, char * text) { +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; - /* this won't increase the size of the listbox! */ + 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; - newtListitemSet(li->items[num], text); - co->ops->draw(co); + 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 newtListboxAddEntry(newtComponent co, char * text, void * data) { +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 (li->numItems == li->allocedItems) { - li->allocedItems += 5; - li->items = realloc(li->items, li->allocedItems * sizeof(*li->items)); + 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 (li->numItems) - li->items[li->numItems] = newtListitem(co->left, - li->numItems + co->top, - text, 0, - li->items[li->numItems - 1], - data); - else - li->items[li->numItems] = newtListitem(co->left, - li->numItems + co->top, - text, 0, NULL, data); + 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; - newtFormAddComponent(li->form, li->items[li->numItems]); + for(i = 0, item = li->boxItems; item != NULL && i < num; + i++, item = item->next); + + if (item) + 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++; - co->height = li->form->height; - co->width = li->form->width; + 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->sb->left = co->left + co->width - li->bdxAdjust - 1; + li->numItems++; + + listboxDraw(co); + + return 0; } -static void listboxDraw(newtComponent co) { +int newtListboxDeleteEntry(newtComponent co, void * key) { struct listbox * li = co->data; + int widest = 0, t; + struct items *item, *item2 = NULL; + int num; - li->form->ops->draw(li->form); + 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; } -static struct eventResult listboxEvent(newtComponent co, struct event ev) { +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 ((li->flags & NEWT_LISTBOX_RETURNEXIT) && ev.when == EV_NORMAL && - ev.event == EV_KEYPRESS && ev.u.key == '\r') { - er.result = ER_EXITFORM; + if(ev.when == EV_EARLY || ev.when == EV_LATE) { return er; } - return li->form->ops->event(li->form, ev); + 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->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; + + 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); - li->form->ops->destroy(li->form); - free(li->items); free(li); free(co); }