From 67be25fbe43274340de89049fec7098cdf998b47 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:09:35 +0300 Subject: Basic working bemenu with curses backend --- lib/menu.c | 527 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 527 insertions(+) create mode 100644 lib/menu.c (limited to 'lib/menu.c') diff --git a/lib/menu.c b/lib/menu.c new file mode 100644 index 0000000..50311cf --- /dev/null +++ b/lib/menu.c @@ -0,0 +1,527 @@ +/** + * @file bemenu.c + */ + +#include "internal.h" +#include +#include +#include +#include + +static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *count, unsigned int *selected) = { + _bmFilterDmenu, + _bmFilterDmenuCaseInsensitive +}; + +static void _bmMenuFilter(bmMenu *menu) +{ + assert(menu != NULL); + + if (menu->filteredItems) + free(menu->filteredItems); + + menu->filteredCount = 0; + menu->filteredItems = NULL; + + unsigned int count, selected; + bmItem **filtered = filterFunc[menu->filterMode](menu, &count, &selected); + + menu->filteredItems = filtered; + menu->filteredCount = count; + menu->index = selected; +} + +static int _bmMenuGrowItems(bmMenu *menu) +{ + void *tmp; + static const unsigned int step = 32; + unsigned int nsize = sizeof(bmItem*) * (menu->allocatedCount + step); + + if (!(tmp = realloc(menu->items, nsize))) { + if (!(tmp = malloc(nsize))) + return 0; + + memcpy(tmp, menu->items, sizeof(bmItem*) * menu->allocatedCount); + } + + menu->items = tmp; + menu->allocatedCount += step; + memset(&menu->items[menu->itemsCount], 0, sizeof(bmItem*) * (menu->allocatedCount - menu->itemsCount)); + return 1; +} + +/** + * Create new bmMenu instance. + * + * @param drawMode Render method to be used for this menu instance. + * @return bmMenu for new menu instance, NULL if creation failed. + */ +bmMenu* bmMenuNew(bmDrawMode drawMode) +{ + bmMenu *menu = calloc(1, sizeof(bmMenu)); + + menu->drawMode = drawMode; + + if (!menu) + return NULL; + + int status = 1; + + switch (menu->drawMode) { + case BM_DRAW_MODE_CURSES: + status = _bmDrawCursesInit(&menu->renderApi); + break; + + default:break; + } + + if (status == 0) { + bmMenuFree(menu); + return NULL; + } + + return menu; +} + +/** + * Release bmMenu instance. + * + * @param menu bmMenu instance to be freed from memory. + */ +void bmMenuFree(bmMenu *menu) +{ + assert(menu != NULL); + + if (menu->renderApi.free) + menu->renderApi.free(); + + if (menu->title) + free(menu->title); + + bmMenuFreeItems(menu); + free(menu); +} + +/** + * Release items inside bmMenu instance. + * + * @param menu bmMenu instance which items will be freed from memory. + */ +void bmMenuFreeItems(bmMenu *menu) +{ + assert(menu != NULL); + + unsigned int i; + for (i = 0; i < menu->itemsCount; ++i) + bmItemFree(menu->items[i]); + + free(menu->items); + menu->allocatedCount = menu->itemsCount = 0; + menu->items = NULL; +} + +/** + * Set active filter mode to bmMenu instance. + * + * @param menu bmMenu instance where to set filter mode. + * @param mode bmFilterMode constant. + */ +void bmMenuSetFilterMode(bmMenu *menu, bmFilterMode mode) +{ + assert(menu != NULL); + + bmFilterMode oldMode = mode; + menu->filterMode = (mode >= BM_FILTER_MODE_LAST ? BM_FILTER_MODE_DMENU : mode); + + if (oldMode != mode) + _bmMenuFilter(menu); +} + +/** + * Get active filter mode from bmMenu instance. + * + * @param menu bmMenu instance where to get filter mode. + * @return bmFilterMode constant. + */ +bmFilterMode bmMenuGetFilterMode(const bmMenu *menu) +{ + assert(menu != NULL); + return menu->filterMode; +} + +/** + * Set title to bmMenu instance. + * + * @param menu bmMenu instance where to set title. + * @param title C "string" to set as title, can be NULL for empty title. + */ +int bmMenuSetTitle(bmMenu *menu, const char *title) +{ + assert(menu != NULL); + + char *copy = NULL; + if (title && !(copy = _bmStrdup(title))) + return 0; + + if (menu->title) + free(menu->title); + + menu->title = copy; + return 1; +} + +/** + * Get title from bmMenu instance. + * + * @param menu bmMenu instance where to get title from. + * @return Pointer to null terminated C "string", can be NULL for empty title. + */ +const char* bmMenuGetTitle(const bmMenu *menu) +{ + assert(menu != NULL); + return menu->title; +} + +/** + * Add item to bmMenu instance at specific index. + * + * @param menu bmMenu instance where item will be added. + * @param item bmItem instance to add. + * @param index Index where item will be added. + * @return 1 on successful add, 0 on failure. + */ +int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) +{ + assert(menu != NULL); + assert(item != NULL); + + if (menu->itemsCount >= menu->allocatedCount && !_bmMenuGrowItems(menu)) + return 0; + + if (index + 1 != menu->itemsCount) { + unsigned int i = index; + memmove(&menu->items[i + 1], &menu->items[i], sizeof(bmItem*) * (menu->itemsCount - i)); + } + + menu->items[index] = item; + menu->itemsCount++; + return 1; +} + +/** + * Add item to bmMenu instance. + * + * @param menu bmMenu instance where item will be added. + * @param item bmItem instance to add. + * @return 1 on successful add, 0 on failure. + */ +int bmMenuAddItem(bmMenu *menu, bmItem *item) +{ + return bmMenuAddItemAt(menu, item, menu->itemsCount); +} + +/** + * Remove item from bmMenu instance at specific index. + * + * @param menu bmMenu instance from where item will be removed. + * @param index Index of item to remove. + * @return 1 on successful add, 0 on failure. + */ +int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) +{ + assert(menu != NULL); + + unsigned int i = index; + if (i >= menu->itemsCount) + return 0; + + memmove(&menu->items[i], &menu->items[i], sizeof(bmItem*) * (menu->itemsCount - i)); + return 1; +} + +/** + * Remove item from bmMenu instance. + * + * @param menu bmMenu instance from where item will be removed. + * @param item bmItem instance to remove. + * @return 1 on successful add, 0 on failure. + */ +int bmMenuRemoveItem(bmMenu *menu, bmItem *item) +{ + assert(menu != NULL); + assert(item != NULL); + + unsigned int i; + for (i = 0; i < menu->itemsCount && menu->items[i] != item; ++i); + return bmMenuRemoveItemAt(menu, i); +} + +/** + * Get selected item from bmMenu instance. + * + * @param menu bmMenu instance from where to get selected item. + * @return Selected bmItem instance, NULL if none selected. + */ +bmItem* bmMenuGetSelectedItem(const bmMenu *menu) +{ + assert(menu != NULL); + + unsigned int count; + bmItem **items = bmMenuGetFilteredItems(menu, &count); + + if (!items || count < menu->index) + return NULL; + + return items[menu->index]; +} + +/** + * Get items from bmMenu instance. + * + * @param menu bmMenu instance from where to get items. + * @param nmemb Reference to unsigned int where total count of returned items will be stored. + * @return Pointer to array of bmItem pointers. + */ +bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *nmemb) +{ + assert(menu != NULL); + + if (nmemb) + *nmemb = menu->itemsCount; + + return menu->items; +} + +/** + * Get filtered (displayed) items from bmMenu instance. + * + * @warning The pointer returned by this function _will_ be invalid when menu internally filters its list again. + * Do not store this pointer. + * + * @param menu bmMenu instance from where to get filtered items. + * @param nmemb Reference to unsigned int where total count of returned items will be stored. + * @return Pointer to array of bmItem pointers. + */ +bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *nmemb) +{ + assert(menu != NULL); + + if (nmemb) + *nmemb = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); + + return (menu->filteredItems ? menu->filteredItems : menu->items); +} + +/** + * Set items to bmMenu instance. + * Will replace all the old items on bmMenu instance. + * + * @param menu bmMenu instance where items will be set. + * @param items Array of bmItem pointers to set. + * @param nmemb Total count of items in array. + * @return 1 on successful set, 0 on failure. + */ +int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) +{ + assert(menu != NULL); + + if (items == NULL || nmemb == 0) { + bmMenuFreeItems(menu); + return 1; + } + + bmItem **newItems; + if (!(newItems = calloc(sizeof(bmItem*), nmemb))) + return 0; + + memcpy(newItems, items, sizeof(bmItem*) * nmemb); + bmMenuFreeItems(menu); + + menu->items = newItems; + menu->allocatedCount = menu->itemsCount = nmemb; + return 1; +} + +/** + * Create new bmMenu instance. + * + * @param drawMode Render method to be used for this menu instance. + * @return bmMenu for new menu instance, NULL if creation failed. + */ +void bmMenuRender(const bmMenu *menu) +{ + assert(menu != NULL); + + if (menu->renderApi.render) + menu->renderApi.render(menu); +} + +/** + * Poll key and unicode from underlying UI toolkit. + * + * This function will block on CURSES draw mode. + * + * @param menu bmMenu instance from which to poll. + * @param unicode Reference to unsigned int. + * @return bmKey for polled key. + */ +bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode) +{ + assert(menu != NULL); + assert(unicode != NULL); + + *unicode = 0; + bmKey key = BM_KEY_NONE; + + if (menu->renderApi.getKey) + key = menu->renderApi.getKey(unicode); + + return key; +} + +/** + * Advances menu logic with key and unicode as input. + * + * @param menu bmMenu instance to be advanced. + * @return bmRunResult for menu state. + */ +bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) +{ + assert(menu != NULL); + char *oldFilter = _bmStrdup(menu->filter); + unsigned int itemsCount = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); + + switch (key) { + case BM_KEY_LEFT: + { + unsigned int oldCursor = menu->cursor; + menu->cursor -= _bmUtf8RunePrev(menu->filter, menu->cursor); + menu->cursesCursor -= _bmUtf8RuneWidth(menu->filter + menu->cursor, oldCursor - menu->cursor); + } + break; + + case BM_KEY_RIGHT: + { + unsigned int oldCursor = menu->cursor; + menu->cursor += _bmUtf8RuneNext(menu->filter, menu->cursor); + menu->cursesCursor += _bmUtf8RuneWidth(menu->filter + oldCursor, menu->cursor - oldCursor); + } + break; + + case BM_KEY_HOME: + menu->cursesCursor = menu->cursor = 0; + break; + + case BM_KEY_END: + menu->cursor = strlen(menu->filter); + menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); + break; + + case BM_KEY_UP: + if (menu->index > 0) + menu->index--; + break; + + case BM_KEY_DOWN: + if (menu->index < itemsCount - 1) + menu->index++; + break; + + case BM_KEY_PAGE_UP: + menu->index = 0; + break; + + case BM_KEY_PAGE_DOWN: + menu->index = itemsCount - 1; + break; + + case BM_KEY_BACKSPACE: + { + size_t width; + menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); + menu->cursesCursor -= width; + } + break; + + case BM_KEY_DELETE: + _bmUtf8RuneRemove(menu->filter, menu->cursor + 1, NULL); + break; + + case BM_KEY_LINE_DELETE_LEFT: + { + while (menu->cursor > 0) { + size_t width; + menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); + menu->cursesCursor -= width; + } + } + break; + + case BM_KEY_LINE_DELETE_RIGHT: + menu->filter[menu->cursor] = 0; + break; + + case BM_KEY_WORD_DELETE: + { + while (menu->cursor < strlen(menu->filter) && !isspace(menu->filter[menu->cursor])) { + unsigned int oldCursor = menu->cursor; + menu->cursor += _bmUtf8RuneNext(menu->filter, menu->cursor); + menu->cursesCursor += _bmUtf8RuneWidth(menu->filter + oldCursor, menu->cursor - oldCursor); + } + while (menu->cursor > 0 && isspace(menu->filter[menu->cursor - 1])) { + unsigned int oldCursor = menu->cursor; + menu->cursor -= _bmUtf8RunePrev(menu->filter, menu->cursor); + menu->cursesCursor -= _bmUtf8RuneWidth(menu->filter + menu->cursor, oldCursor - menu->cursor); + } + while (menu->cursor > 0 && !isspace(menu->filter[menu->cursor - 1])) { + size_t width; + menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); + menu->cursesCursor -= width; + } + } + break; + + case BM_KEY_UNICODE: + { + size_t width; + menu->cursor += _bmUnicodeInsert(menu->filter, sizeof(menu->filter) - 1, menu->cursor, unicode, &width); + menu->cursesCursor += width; + } + break; + + case BM_KEY_TAB: + { + bmItem *selected = bmMenuGetSelectedItem(menu); + if (selected && bmItemGetText(selected)) { + const char *text = bmItemGetText(selected); + size_t len = strlen(text); + + if (len > sizeof(menu->filter) - 1) + len = sizeof(menu->filter) - 1; + + memset(menu->filter, 0, strlen(menu->filter)); + memcpy(menu->filter, text, len); + menu->cursor = strlen(menu->filter); + menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); + } + } + break; + + case BM_KEY_RETURN: + return BM_RUN_RESULT_SELECTED; + + case BM_KEY_ESCAPE: + return BM_RUN_RESULT_CANCEL; + + default: break; + } + + if (oldFilter && strcmp(oldFilter, menu->filter)) + _bmMenuFilter(menu); + + if (oldFilter) + free(oldFilter); + return BM_RUN_RESULT_RUNNING; +} + +/* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From 944e36b21d1bbc80680e228daa75f27b3eab4e4b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:25:37 +0300 Subject: Free filtered items. --- lib/menu.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/menu.c') diff --git a/lib/menu.c b/lib/menu.c index 50311cf..7a39746 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -98,6 +98,9 @@ void bmMenuFree(bmMenu *menu) if (menu->title) free(menu->title); + if (menu->filteredItems) + free(menu->filteredItems); + bmMenuFreeItems(menu); free(menu); } -- cgit v1.2.3-70-g09d2 From dd4bddcf9877c5cf2f01cc035417f779565a0267 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:25:46 +0300 Subject: Do bounds checking correctly. --- lib/menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/menu.c') diff --git a/lib/menu.c b/lib/menu.c index 7a39746..b6116e9 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -272,7 +272,7 @@ bmItem* bmMenuGetSelectedItem(const bmMenu *menu) unsigned int count; bmItem **items = bmMenuGetFilteredItems(menu, &count); - if (!items || count < menu->index) + if (!items || count <= menu->index) return NULL; return items[menu->index]; -- cgit v1.2.3-70-g09d2 From 2615c43dc052617312ea102ab2a50cb449d9827b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:26:00 +0300 Subject: Move side effects after cleanup. --- lib/menu.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'lib/menu.c') diff --git a/lib/menu.c b/lib/menu.c index b6116e9..5ea046a 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -510,12 +510,6 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) } break; - case BM_KEY_RETURN: - return BM_RUN_RESULT_SELECTED; - - case BM_KEY_ESCAPE: - return BM_RUN_RESULT_CANCEL; - default: break; } @@ -524,6 +518,13 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) if (oldFilter) free(oldFilter); + + switch (key) { + case BM_KEY_RETURN: return BM_RUN_RESULT_SELECTED; + case BM_KEY_ESCAPE: return BM_RUN_RESULT_CANCEL; + default: break; + } + return BM_RUN_RESULT_RUNNING; } -- cgit v1.2.3-70-g09d2 From 4d920ad9e4aa344c1600e7419f75f6adb647b1b0 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:41:32 +0300 Subject: Make asserts and ifs more consistent. --- client/client.c | 10 +++++----- lib/draw/curses.c | 2 +- lib/filter.c | 12 ++++++------ lib/item.c | 6 +++--- lib/menu.c | 42 +++++++++++++++++++++--------------------- lib/util.c | 19 +++++++++++-------- test/bmMenuNew.c | 2 +- 7 files changed, 48 insertions(+), 45 deletions(-) (limited to 'lib/menu.c') diff --git a/client/client.c b/client/client.c index df56511..973e57a 100644 --- a/client/client.c +++ b/client/client.c @@ -17,22 +17,22 @@ static ptrdiff_t getLine(char **outLine, size_t *outAllocated, FILE *stream) size_t len = 0, allocated; char *s, *buffer; - assert(outLine != NULL); - assert(outAllocated != NULL); + assert(outLine); + assert(outAllocated); - if (stream == NULL || feof(stream) || ferror(stream)) + if (!stream || feof(stream) || ferror(stream)) return -1; allocated = *outAllocated; buffer = *outLine; - if (buffer == NULL || allocated == 0) { + if (!buffer || allocated == 0) { if (!(buffer = calloc(1, (allocated = 1024) + 1))) return -1; } for (s = buffer;;) { - if (fgets(s, allocated - (s - buffer), stream) == NULL) { + if (!fgets(s, allocated - (s - buffer), stream)) { *outAllocated = allocated; *outLine = buffer; return -1; diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 5c6ff4a..3582be5 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -139,7 +139,7 @@ static void _bmDrawCursesEndWin(void) static bmKey _bmDrawCursesGetKey(unsigned int *unicode) { - assert(unicode != NULL); + assert(unicode); *unicode = 0; if (!curses.stdscr) diff --git a/lib/filter.c b/lib/filter.c index b1cf668..d301415 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -17,9 +17,9 @@ */ bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selected) { - assert(menu != NULL); - assert(count != NULL); - assert(selected != NULL); + assert(menu); + assert(count); + assert(selected); *count = *selected = 0; /* FIXME: not real dmenu like filtering at all */ @@ -51,9 +51,9 @@ bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selecte */ bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *count, unsigned int *selected) { - assert(menu != NULL); - assert(count != NULL); - assert(selected != NULL); + assert(menu); + assert(count); + assert(selected); *count = *selected = 0; /* FIXME: stub */ diff --git a/lib/item.c b/lib/item.c index 40ebb52..beca6ed 100644 --- a/lib/item.c +++ b/lib/item.c @@ -31,7 +31,7 @@ bmItem* bmItemNew(const char *text) */ void bmItemFree(bmItem *item) { - assert(item != NULL); + assert(item); if (item->text) free(item->text); @@ -47,7 +47,7 @@ void bmItemFree(bmItem *item) */ int bmItemSetText(bmItem *item, const char *text) { - assert(item != NULL); + assert(item); char *copy = NULL; if (text && !(copy = _bmStrdup(text))) @@ -68,7 +68,7 @@ int bmItemSetText(bmItem *item, const char *text) */ const char* bmItemGetText(const bmItem *item) { - assert(item != NULL); + assert(item); return item->text; } diff --git a/lib/menu.c b/lib/menu.c index 5ea046a..ccdbf0d 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -15,7 +15,7 @@ static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *c static void _bmMenuFilter(bmMenu *menu) { - assert(menu != NULL); + assert(menu); if (menu->filteredItems) free(menu->filteredItems); @@ -90,7 +90,7 @@ bmMenu* bmMenuNew(bmDrawMode drawMode) */ void bmMenuFree(bmMenu *menu) { - assert(menu != NULL); + assert(menu); if (menu->renderApi.free) menu->renderApi.free(); @@ -112,7 +112,7 @@ void bmMenuFree(bmMenu *menu) */ void bmMenuFreeItems(bmMenu *menu) { - assert(menu != NULL); + assert(menu); unsigned int i; for (i = 0; i < menu->itemsCount; ++i) @@ -131,7 +131,7 @@ void bmMenuFreeItems(bmMenu *menu) */ void bmMenuSetFilterMode(bmMenu *menu, bmFilterMode mode) { - assert(menu != NULL); + assert(menu); bmFilterMode oldMode = mode; menu->filterMode = (mode >= BM_FILTER_MODE_LAST ? BM_FILTER_MODE_DMENU : mode); @@ -148,7 +148,7 @@ void bmMenuSetFilterMode(bmMenu *menu, bmFilterMode mode) */ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu) { - assert(menu != NULL); + assert(menu); return menu->filterMode; } @@ -160,7 +160,7 @@ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu) */ int bmMenuSetTitle(bmMenu *menu, const char *title) { - assert(menu != NULL); + assert(menu); char *copy = NULL; if (title && !(copy = _bmStrdup(title))) @@ -181,7 +181,7 @@ int bmMenuSetTitle(bmMenu *menu, const char *title) */ const char* bmMenuGetTitle(const bmMenu *menu) { - assert(menu != NULL); + assert(menu); return menu->title; } @@ -195,8 +195,8 @@ const char* bmMenuGetTitle(const bmMenu *menu) */ int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) { - assert(menu != NULL); - assert(item != NULL); + assert(menu); + assert(item); if (menu->itemsCount >= menu->allocatedCount && !_bmMenuGrowItems(menu)) return 0; @@ -232,7 +232,7 @@ int bmMenuAddItem(bmMenu *menu, bmItem *item) */ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) { - assert(menu != NULL); + assert(menu); unsigned int i = index; if (i >= menu->itemsCount) @@ -251,8 +251,8 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) */ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) { - assert(menu != NULL); - assert(item != NULL); + assert(menu); + assert(item); unsigned int i; for (i = 0; i < menu->itemsCount && menu->items[i] != item; ++i); @@ -267,7 +267,7 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) */ bmItem* bmMenuGetSelectedItem(const bmMenu *menu) { - assert(menu != NULL); + assert(menu); unsigned int count; bmItem **items = bmMenuGetFilteredItems(menu, &count); @@ -287,7 +287,7 @@ bmItem* bmMenuGetSelectedItem(const bmMenu *menu) */ bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *nmemb) { - assert(menu != NULL); + assert(menu); if (nmemb) *nmemb = menu->itemsCount; @@ -307,7 +307,7 @@ bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *nmemb) */ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *nmemb) { - assert(menu != NULL); + assert(menu); if (nmemb) *nmemb = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); @@ -326,9 +326,9 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *nmemb) */ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) { - assert(menu != NULL); + assert(menu); - if (items == NULL || nmemb == 0) { + if (!items || nmemb == 0) { bmMenuFreeItems(menu); return 1; } @@ -353,7 +353,7 @@ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) */ void bmMenuRender(const bmMenu *menu) { - assert(menu != NULL); + assert(menu); if (menu->renderApi.render) menu->renderApi.render(menu); @@ -370,8 +370,8 @@ void bmMenuRender(const bmMenu *menu) */ bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode) { - assert(menu != NULL); - assert(unicode != NULL); + assert(menu); + assert(unicode); *unicode = 0; bmKey key = BM_KEY_NONE; @@ -390,7 +390,7 @@ bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode) */ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) { - assert(menu != NULL); + assert(menu); char *oldFilter = _bmStrdup(menu->filter); unsigned int itemsCount = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); diff --git a/lib/util.c b/lib/util.c index 8c9bc0f..d948b05 100644 --- a/lib/util.c +++ b/lib/util.c @@ -18,6 +18,8 @@ */ char* _bmStrdup(const char *string) { + assert(string); + size_t len = strlen(string); if (len == 0) return NULL; @@ -41,6 +43,8 @@ char* _bmStrdup(const char *string) */ bmItem** _bmShrinkItemList(bmItem ***list, size_t osize, size_t nsize) { + assert(list); + if (nsize >= osize) return *list; @@ -62,8 +66,7 @@ bmItem** _bmShrinkItemList(bmItem ***list, size_t osize, size_t nsize) */ int _bmUtf8StringScreenWidth(const char *string) { - if (!string) - return 0; + assert(string); int num_char = mbstowcs(NULL, string, 0) + 1; wchar_t *wstring = malloc((num_char + 1) * sizeof (wstring[0])); @@ -87,7 +90,7 @@ int _bmUtf8StringScreenWidth(const char *string) */ size_t _bmUtf8RuneNext(const char *string, size_t start) { - assert(string != NULL); + assert(string); size_t len = strlen(string), i = start; if (len == 0 || len <= i || !*string) @@ -106,7 +109,7 @@ size_t _bmUtf8RuneNext(const char *string, size_t start) */ size_t _bmUtf8RunePrev(const char *string, size_t start) { - assert(string != NULL); + assert(string); size_t len = strlen(string), i = start; if (i == 0 || len < start || !*string) @@ -125,7 +128,7 @@ size_t _bmUtf8RunePrev(const char *string, size_t start) */ size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len) { - assert(rune != NULL); + assert(rune); char mb[5] = { 0, 0, 0, 0, 0 }; memcpy(mb, rune, (u8len > 4 ? 4 : u8len)); return _bmUtf8StringScreenWidth(mb); @@ -141,7 +144,7 @@ size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len) */ size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *runeWidth) { - assert(string != NULL); + assert(string); if (runeWidth) *runeWidth = 0; @@ -173,7 +176,7 @@ size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *runeWidth) */ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *runeWidth) { - assert(string != NULL); + assert(string); if (runeWidth) *runeWidth = 0; @@ -206,7 +209,7 @@ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char */ size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *runeWidth) { - assert(string != NULL); + assert(string); char u8len = ((unicode < 0x80) ? 1 : ((unicode < 0x800) ? 2 : ((unicode < 0x10000) ? 3 : 4))); char mb[5] = { 0, 0, 0, 0 }; diff --git a/test/bmMenuNew.c b/test/bmMenuNew.c index e1b6161..ccb572d 100644 --- a/test/bmMenuNew.c +++ b/test/bmMenuNew.c @@ -11,7 +11,7 @@ int main(int argc, char **argv) bmDrawMode i; for (i = 0; i < BM_DRAW_MODE_LAST; ++i) { bmMenu *menu = bmMenuNew(i); - assert(menu != NULL); + assert(menu); bmMenuRender(menu); bmMenuFree(menu); } -- cgit v1.2.3-70-g09d2 From 722af2f7a0fe0e437e233cfaf7acadc3bcd0f24e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 11:07:37 +0300 Subject: Clarify which enums the functions are mapped to. --- lib/menu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib/menu.c') diff --git a/lib/menu.c b/lib/menu.c index ccdbf0d..184c976 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -8,9 +8,12 @@ #include #include +/** + * Filter function map. + */ static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *count, unsigned int *selected) = { - _bmFilterDmenu, - _bmFilterDmenuCaseInsensitive + _bmFilterDmenu, /* BM_FILTER_DMENU */ + _bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */ }; static void _bmMenuFilter(bmMenu *menu) -- cgit v1.2.3-70-g09d2 From ff8ad3a804459ddc4e407b688b093b6f4543cf53 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 11:08:10 +0300 Subject: Remove @file from non-public library files. --- lib/draw/curses.c | 4 ---- lib/filter.c | 4 ---- lib/internal.h | 4 ---- lib/item.c | 4 ---- lib/menu.c | 4 ---- lib/util.c | 4 ---- 6 files changed, 24 deletions(-) (limited to 'lib/menu.c') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index eecfcba..a0f5351 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -1,7 +1,3 @@ -/** - * @file curses.c - */ - #include "../internal.h" #include #include diff --git a/lib/filter.c b/lib/filter.c index d301415..92e5683 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -1,7 +1,3 @@ -/** - * @file filter.c - */ - #include "internal.h" #include #include diff --git a/lib/internal.h b/lib/internal.h index 786e082..2220333 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -1,7 +1,3 @@ -/** - * @file internal.h - */ - #include "bemenu.h" #ifndef size_t diff --git a/lib/item.c b/lib/item.c index beca6ed..8320b9b 100644 --- a/lib/item.c +++ b/lib/item.c @@ -1,7 +1,3 @@ -/** - * @file item.c - */ - #include "internal.h" #include #include diff --git a/lib/menu.c b/lib/menu.c index 184c976..4ccb94a 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -1,7 +1,3 @@ -/** - * @file bemenu.c - */ - #include "internal.h" #include #include diff --git a/lib/util.c b/lib/util.c index d948b05..ae230c8 100644 --- a/lib/util.c +++ b/lib/util.c @@ -1,7 +1,3 @@ -/** - * @file util.c - */ - #include "internal.h" #define _XOPEN_SOURCE 700 #include -- cgit v1.2.3-70-g09d2 From e230f03e54e0ad0c521177cd7a3f4c7628608d15 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 13:08:26 +0300 Subject: Fix bmMenuRender documentation --- lib/menu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib/menu.c') diff --git a/lib/menu.c b/lib/menu.c index 4ccb94a..29a3058 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -345,10 +345,9 @@ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) } /** - * Create new bmMenu instance. + * Render bmMenu instance using chosen draw method. * - * @param drawMode Render method to be used for this menu instance. - * @return bmMenu for new menu instance, NULL if creation failed. + * @param menu bmMenu instance to be rendered. */ void bmMenuRender(const bmMenu *menu) { -- cgit v1.2.3-70-g09d2 From d53c91393d0c66892d25e4ef96cd23568d9fdcea Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 13:08:38 +0300 Subject: Fix bmMenuRunWithKey documentation --- lib/bemenu.h | 2 ++ lib/menu.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'lib/menu.c') diff --git a/lib/bemenu.h b/lib/bemenu.h index b4b9b30..d8e604b 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -227,6 +227,8 @@ bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode); * Advances menu logic with key and unicode as input. * * @param menu bmMenu instance to be advanced. + * @param key Key input that will advance menu logic. + * @param unicode Unicode input that will advance menu logic. * @return bmRunResult for menu state. */ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode); diff --git a/lib/menu.c b/lib/menu.c index 29a3058..c8c6a26 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -384,6 +384,8 @@ bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode) * Advances menu logic with key and unicode as input. * * @param menu bmMenu instance to be advanced. + * @param key Key input that will advance menu logic. + * @param unicode Unicode input that will advance menu logic. * @return bmRunResult for menu state. */ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) -- cgit v1.2.3-70-g09d2 From 8631506d9d1522f1a24e528f75682201b4f3cc7b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 17:26:42 +0300 Subject: Update documentation, include only public API in doxygen output. --- client/client.c | 16 ------- doxygen/Doxyfile.in | 6 +-- doxygen/Mainpage.dox | 2 + lib/bemenu.h | 119 ++++++++++++++++++++++++++++++++++++++++----------- lib/filter.c | 28 ++++++------ lib/internal.h | 13 +++--- lib/item.c | 8 ++-- lib/menu.c | 46 ++++++++++++-------- lib/util.c | 46 ++++++++++---------- 9 files changed, 172 insertions(+), 112 deletions(-) (limited to 'lib/menu.c') diff --git a/client/client.c b/client/client.c index 973e57a..4419967 100644 --- a/client/client.c +++ b/client/client.c @@ -1,10 +1,3 @@ -/** - * @file client.c - * - * Sample client using the libbemenu. - * Also usable as dmenu replacement. - */ - #include #include #include @@ -86,15 +79,6 @@ static void readItemsToMenuFromStdin(bmMenu *menu) free(line); } -/** - * Main method - * - * This function gives and takes the life of our program. - * - * @param argc Number of arguments from command line - * @param argv Pointer to array of the arguments - * @return exit status of the program - */ int main(int argc, char **argv) { (void)argc, (void)argv; diff --git a/doxygen/Doxyfile.in b/doxygen/Doxyfile.in index fcd3604..3b80187 100644 --- a/doxygen/Doxyfile.in +++ b/doxygen/Doxyfile.in @@ -90,7 +90,7 @@ OUTPUT_LANGUAGE = English # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. -BRIEF_MEMBER_DESC = NO +BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief # description of a member or function before the detailed description @@ -765,7 +765,7 @@ INPUT_ENCODING = UTF-8 # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. -FILE_PATTERNS = +FILE_PATTERNS = *.h *.dox # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -796,7 +796,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = internal.h # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the diff --git a/doxygen/Mainpage.dox b/doxygen/Mainpage.dox index 8da4792..869071c 100644 --- a/doxygen/Mainpage.dox +++ b/doxygen/Mainpage.dox @@ -16,4 +16,6 @@ Features: - Curses bemenu also provides 'bemenu' executable that is compatible with dmenu interface. + +Get started on the Modules page. */ diff --git a/lib/bemenu.h b/lib/bemenu.h index d8e604b..f2f0372 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -1,13 +1,34 @@ /** * @file bemenu.h * - * Public header + * Public API header. */ +typedef struct _bmMenu bmMenu; +typedef struct _bmItem bmItem; + +/** + * @defgroup Menu + * @brief Menu container. + * + * Holds all the items, runs logic and gets rendered. + */ + +/** + * @defgroup Item + * @brief Item container. + * + * Contains properties for visual representation of item. + */ + +/** + * @addtogroup Menu + * @{ */ + /** * Draw mode constants for bmMenu instance draw mode. * - * BM_DRAW_MODE_LAST is provided for enumerating draw modes. + * @link ::bmDrawMode BM_DRAW_MODE_LAST @endlink is provided for enumerating draw modes. * Instancing with it however provides exactly same functionality as BM_DRAW_MODE_NONE. */ typedef enum bmDrawMode { @@ -19,7 +40,7 @@ typedef enum bmDrawMode { /** * Filter mode constants for bmMenu instance filter mode. * - * BM_FILTER_MODE_LAST is provided for enumerating filter modes. + * @link ::bmFilterMode BM_FILTER_MODE_LAST @endlink is provided for enumerating filter modes. * Using it as filter mode however provides exactly same functionality as BM_FILTER_MODE_DMENU. */ typedef enum bmFilterMode { @@ -31,9 +52,9 @@ typedef enum bmFilterMode { /** * Result constants from bmMenuRunWithKey function. * - * BM_RUN_RESULT_RUNNING means that menu is running and thus should be still renderer && ran. - * BM_RUN_RESULT_SELECTED means that menu was closed and items were selected. - * BM_RUN_RESULT_CANCEL means that menu was closed and selection was canceled. + * - @link ::bmRunResult BM_RUN_RESULT_RUNNING @endlink means that menu is running and thus should be still renderer && ran. + * - @link ::bmRunResult BM_RUN_RESULT_SELECTED @endlink means that menu was closed and items were selected. + * - @link ::bmRunResult BM_RUN_RESULT_CANCEL @endlink means that menu was closed and selection was canceled. */ typedef enum bmRunResult { BM_RUN_RESULT_RUNNING, @@ -44,7 +65,7 @@ typedef enum bmRunResult { /** * Key constants. * - * BM_KEY_LAST is provided for enumerating keys. + * @link ::bmKey BM_KEY_LAST @endlink is provided for enumerating keys. */ typedef enum bmKey { BM_KEY_NONE, @@ -68,14 +89,15 @@ typedef enum bmKey { BM_KEY_LAST } bmKey; -typedef struct _bmMenu bmMenu; -typedef struct _bmItem bmItem; +/** + * @name Menu Memory + * @{ */ /** * Create new bmMenu instance. * * @param drawMode Render method to be used for this menu instance. - * @return bmMenu for new menu instance, NULL if creation failed. + * @return bmMenu for new menu instance, **NULL** if creation failed. */ bmMenu* bmMenuNew(bmDrawMode drawMode); @@ -93,6 +115,12 @@ void bmMenuFree(bmMenu *menu); */ void bmMenuFreeItems(bmMenu *menu); +/** @} Menu Memory */ + +/** + * @name Menu Properties + * @{ */ + /** * Set active filter mode to bmMenu instance. * @@ -113,7 +141,7 @@ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu); * Set title to bmMenu instance. * * @param menu bmMenu instance where to set title. - * @param title C "string" to set as title, can be NULL for empty title. + * @param title C "string" to set as title, can be **NULL** for empty title. */ int bmMenuSetTitle(bmMenu *menu, const char *title); @@ -121,10 +149,16 @@ int bmMenuSetTitle(bmMenu *menu, const char *title); * Get title from bmMenu instance. * * @param menu bmMenu instance where to get title from. - * @return Pointer to null terminated C "string", can be NULL for empty title. + * @return Pointer to null terminated C "string", can be **NULL** for empty title. */ const char* bmMenuGetTitle(const bmMenu *menu); +/** @} Properties */ + +/** + * @name Menu Items + * @{ */ + /** * Add item to bmMenu instance at specific index. * @@ -147,6 +181,8 @@ int bmMenuAddItem(bmMenu *menu, bmItem *item); /** * Remove item from bmMenu instance at specific index. * + * @warning The item won't be freed, use bmItemFree to do that. + * * @param menu bmMenu instance from where item will be removed. * @param index Index of item to remove. * @return 1 on successful add, 0 on failure. @@ -155,7 +191,8 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index); /** * Remove item from bmMenu instance. - * The item won't be freed, use bmItemFree to do that. + * + * @warning The item won't be freed, use bmItemFree to do that. * * @param menu bmMenu instance from where item will be removed. * @param item bmItem instance to remove. @@ -167,18 +204,20 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item); * Get selected item from bmMenu instance. * * @param menu bmMenu instance from where to get selected item. - * @return Selected bmItem instance, NULL if none selected. + * @return Selected bmItem instance, **NULL** if none selected. */ bmItem* bmMenuGetSelectedItem(const bmMenu *menu); /** * Get items from bmMenu instance. * + * @warning The pointer returned by this function may be invalid after removing or adding new items. + * * @param menu bmMenu instance from where to get items. - * @param nmemb Reference to unsigned int where total count of returned items will be stored. + * @param outNmemb Reference to unsigned int where total count of returned items will be stored. * @return Pointer to array of bmItem pointers. */ -bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *nmemb); +bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb); /** * Get filtered (displayed) items from bmMenu instance. @@ -187,16 +226,16 @@ bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *nmemb); * Do not store this pointer. * * @param menu bmMenu instance from where to get filtered items. - * @param nmemb Reference to unsigned int where total count of returned items will be stored. + * @param outNmemb Reference to unsigned int where total count of returned items will be stored. * @return Pointer to array of bmItem pointers. */ -bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *nmemb); +bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb); /** * Set items to bmMenu instance. * Will replace all the old items on bmMenu instance. * - * If items is NULL, or nmemb is zero, all items will be freed from the menu. + * If items is **NULL**, or nmemb is zero, all items will be freed from the menu. * * @param menu bmMenu instance where items will be set. * @param items Array of bmItem pointers to set. @@ -205,6 +244,12 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *nmemb); */ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb); +/** @} Menu Items */ + +/** + * @name Menu Logic + * @{ */ + /** * Render bmMenu instance using chosen draw method. * @@ -215,13 +260,13 @@ void bmMenuRender(const bmMenu *menu); /** * Poll key and unicode from underlying UI toolkit. * - * This function will block on CURSES draw mode. + * This function will block on @link ::bmDrawMode BM_DRAW_MODE_CURSES @endlink draw mode. * * @param menu bmMenu instance from which to poll. - * @param unicode Reference to unsigned int. + * @param outUnicode Reference to unsigned int. * @return bmKey for polled key. */ -bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode); +bmKey bmMenuGetKey(bmMenu *menu, unsigned int *outUnicode); /** * Advances menu logic with key and unicode as input. @@ -233,11 +278,23 @@ bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode); */ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode); +/** @} Menu Logic */ + +/** @} Menu */ + +/** + * @addtogroup Item + * @{ */ + +/** + * @name Item Memory + * @{ */ + /** * Allocate a new item. * - * @param text Pointer to null terminated C "string", can be NULL for empty text. - * @return bmItem for new item instance, NULL if creation failed. + * @param text Pointer to null terminated C "string", can be **NULL** for empty text. + * @return bmItem for new item instance, **NULL** if creation failed. */ bmItem* bmItemNew(const char *text); @@ -248,11 +305,17 @@ bmItem* bmItemNew(const char *text); */ void bmItemFree(bmItem *item); +/** @} Item Memory */ + +/** + * @name Item Properties + * @{ */ + /** * Set text to bmItem instance. * * @param item bmItem instance where to set text. - * @param text C "string" to set as text, can be NULL for empty text. + * @param text C "string" to set as text, can be **NULL** for empty text. */ int bmItemSetText(bmItem *item, const char *text); @@ -260,8 +323,12 @@ int bmItemSetText(bmItem *item, const char *text); * Get text from bmItem instance. * * @param item bmItem instance where to get text from. - * @return Pointer to null terminated C "string", can be NULL for empty text. + * @return Pointer to null terminated C "string", can be **NULL** for empty text. */ const char* bmItemGetText(const bmItem *item); +/** @} Item Properties */ + +/** @} Item */ + /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/filter.c b/lib/filter.c index 92e5683..33126c0 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -7,16 +7,16 @@ * Filter that mimics the vanilla dmenu filtering. * * @param menu bmMenu instance to filter. - * @param count unsigned int reference to filtered items count. - * @param selected unsigned int reference to new selected item index. + * @param outNmemb unsigned int reference to filtered items outNmemb. + * @param outSelected unsigned int reference to new outSelected item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selected) +bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected) { assert(menu); - assert(count); - assert(selected); - *count = *selected = 0; + assert(outNmemb); + assert(outSelected); + *outNmemb = *outSelected = 0; /* FIXME: not real dmenu like filtering at all */ @@ -29,28 +29,28 @@ bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selecte bmItem *item = menu->items[i]; if (item->text && strstr(item->text, menu->filter)) { if (f == 0 || item == bmMenuGetSelectedItem(menu)) - *selected = f; + *outSelected = f; filtered[f++] = item; } } - return _bmShrinkItemList(&filtered, menu->itemsCount, (*count = f)); + return _bmShrinkItemList(&filtered, menu->itemsCount, (*outNmemb = f)); } /** * Filter that mimics the vanilla case-insensitive dmenu filtering. * * @param menu bmMenu instance to filter. - * @param count unsigned int reference to filtered items count. - * @param selected unsigned int reference to new selected item index. + * @param outNmemb unsigned int reference to filtered items outNmemb. + * @param outSelected unsigned int reference to new outSelected item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *count, unsigned int *selected) +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected) { assert(menu); - assert(count); - assert(selected); - *count = *selected = 0; + assert(outNmemb); + assert(outSelected); + *outNmemb = *outSelected = 0; /* FIXME: stub */ diff --git a/lib/internal.h b/lib/internal.h index 41fb926..66eabbc 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -64,7 +64,6 @@ struct _bmMenu { /** * Text used to filter matches. - * * XXX: Change this to a pointer? */ char filter[1024]; @@ -115,18 +114,18 @@ struct _bmMenu { int _bmDrawCursesInit(struct _bmRenderApi *api); /* filter.c */ -bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selected); -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *count, unsigned int *selected); +bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected); +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected); /* util.c */ char* _bmStrdup(const char *s); -bmItem** _bmShrinkItemList(bmItem ***list, size_t osize, size_t nsize); +bmItem** _bmShrinkItemList(bmItem ***inOutList, size_t osize, size_t nsize); int _bmUtf8StringScreenWidth(const char *string); size_t _bmUtf8RuneNext(const char *string, size_t start); size_t _bmUtf8RunePrev(const char *string, size_t start); size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len); -size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *runeWidth); -size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *runeWidth); -size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *runeWidth); +size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *outRuneWidth); +size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *outRuneWidth); +size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *outRuneWidth); /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/item.c b/lib/item.c index 8320b9b..e6ccc7a 100644 --- a/lib/item.c +++ b/lib/item.c @@ -6,8 +6,8 @@ /** * Allocate a new item. * - * @param text Pointer to null terminated C "string", can be NULL for empty text. - * @return bmItem instance. + * @param text Pointer to null terminated C "string", can be **NULL** for empty text. + * @return bmItem for new item instance, **NULL** if creation failed. */ bmItem* bmItemNew(const char *text) { @@ -39,7 +39,7 @@ void bmItemFree(bmItem *item) * Set text to bmItem instance. * * @param item bmItem instance where to set text. - * @param text C "string" to set as text, can be NULL for empty text. + * @param text C "string" to set as text, can be **NULL** for empty text. */ int bmItemSetText(bmItem *item, const char *text) { @@ -60,7 +60,7 @@ int bmItemSetText(bmItem *item, const char *text) * Get text from bmItem instance. * * @param item bmItem instance where to get text from. - * @return Pointer to null terminated C "string", can be NULL for empty text. + * @return Pointer to null terminated C "string", can be **NULL** for empty text. */ const char* bmItemGetText(const bmItem *item) { diff --git a/lib/menu.c b/lib/menu.c index c8c6a26..3d41121 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -7,7 +7,7 @@ /** * Filter function map. */ -static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *count, unsigned int *selected) = { +static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected) = { _bmFilterDmenu, /* BM_FILTER_DMENU */ _bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */ }; @@ -53,7 +53,7 @@ static int _bmMenuGrowItems(bmMenu *menu) * Create new bmMenu instance. * * @param drawMode Render method to be used for this menu instance. - * @return bmMenu for new menu instance, NULL if creation failed. + * @return bmMenu for new menu instance, **NULL** if creation failed. */ bmMenu* bmMenuNew(bmDrawMode drawMode) { @@ -155,7 +155,7 @@ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu) * Set title to bmMenu instance. * * @param menu bmMenu instance where to set title. - * @param title C "string" to set as title, can be NULL for empty title. + * @param title C "string" to set as title, can be **NULL** for empty title. */ int bmMenuSetTitle(bmMenu *menu, const char *title) { @@ -176,7 +176,7 @@ int bmMenuSetTitle(bmMenu *menu, const char *title) * Get title from bmMenu instance. * * @param menu bmMenu instance where to get title from. - * @return Pointer to null terminated C "string", can be NULL for empty title. + * @return Pointer to null terminated C "string", can be **NULL** for empty title. */ const char* bmMenuGetTitle(const bmMenu *menu) { @@ -225,6 +225,8 @@ int bmMenuAddItem(bmMenu *menu, bmItem *item) /** * Remove item from bmMenu instance at specific index. * + * @warning The item won't be freed, use bmItemFree to do that. + * * @param menu bmMenu instance from where item will be removed. * @param index Index of item to remove. * @return 1 on successful add, 0 on failure. @@ -244,6 +246,8 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) /** * Remove item from bmMenu instance. * + * @warning The item won't be freed, use bmItemFree to do that. + * * @param menu bmMenu instance from where item will be removed. * @param item bmItem instance to remove. * @return 1 on successful add, 0 on failure. @@ -262,7 +266,7 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) * Get selected item from bmMenu instance. * * @param menu bmMenu instance from where to get selected item. - * @return Selected bmItem instance, NULL if none selected. + * @return Selected bmItem instance, **NULL** if none selected. */ bmItem* bmMenuGetSelectedItem(const bmMenu *menu) { @@ -280,16 +284,18 @@ bmItem* bmMenuGetSelectedItem(const bmMenu *menu) /** * Get items from bmMenu instance. * + * @warning The pointer returned by this function may be invalid after removing or adding new items. + * * @param menu bmMenu instance from where to get items. - * @param nmemb Reference to unsigned int where total count of returned items will be stored. + * @param outNmemb Reference to unsigned int where total count of returned items will be stored. * @return Pointer to array of bmItem pointers. */ -bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *nmemb) +bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); - if (nmemb) - *nmemb = menu->itemsCount; + if (outNmemb) + *outNmemb = menu->itemsCount; return menu->items; } @@ -301,15 +307,15 @@ bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *nmemb) * Do not store this pointer. * * @param menu bmMenu instance from where to get filtered items. - * @param nmemb Reference to unsigned int where total count of returned items will be stored. + * @param outNmemb Reference to unsigned int where total count of returned items will be stored. * @return Pointer to array of bmItem pointers. */ -bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *nmemb) +bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); - if (nmemb) - *nmemb = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); + if (outNmemb) + *outNmemb = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); return (menu->filteredItems ? menu->filteredItems : menu->items); } @@ -318,6 +324,8 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *nmemb) * Set items to bmMenu instance. * Will replace all the old items on bmMenu instance. * + * If items is **NULL**, or nmemb is zero, all items will be freed from the menu. + * * @param menu bmMenu instance where items will be set. * @param items Array of bmItem pointers to set. * @param nmemb Total count of items in array. @@ -360,22 +368,22 @@ void bmMenuRender(const bmMenu *menu) /** * Poll key and unicode from underlying UI toolkit. * - * This function will block on CURSES draw mode. + * This function will block on @link ::bmDrawMode BM_DRAW_MODE_CURSES @endlink draw mode. * * @param menu bmMenu instance from which to poll. - * @param unicode Reference to unsigned int. + * @param outUnicode Reference to unsigned int. * @return bmKey for polled key. */ -bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode) +bmKey bmMenuGetKey(bmMenu *menu, unsigned int *outUnicode) { assert(menu); - assert(unicode); + assert(outUnicode); - *unicode = 0; + *outUnicode = 0; bmKey key = BM_KEY_NONE; if (menu->renderApi.getKey) - key = menu->renderApi.getKey(unicode); + key = menu->renderApi.getKey(outUnicode); return key; } diff --git a/lib/util.c b/lib/util.c index ae230c8..5eb980d 100644 --- a/lib/util.c +++ b/lib/util.c @@ -37,21 +37,21 @@ char* _bmStrdup(const char *string) * @param nsize New size the list will be shrinked to. * @return Pointer to list of bmItem pointers. */ -bmItem** _bmShrinkItemList(bmItem ***list, size_t osize, size_t nsize) +bmItem** _bmShrinkItemList(bmItem ***inOutList, size_t osize, size_t nsize) { - assert(list); + assert(inOutList); if (nsize >= osize) - return *list; + return *inOutList; void *tmp = malloc(sizeof(bmItem*) * nsize); if (!tmp) - return *list; + return *inOutList; - memcpy(tmp, *list, sizeof(bmItem*) * nsize); - free(*list); - *list = tmp; - return *list; + memcpy(tmp, *inOutList, sizeof(bmItem*) * nsize); + free(*inOutList); + *inOutList = tmp; + return *inOutList; } /** @@ -135,15 +135,15 @@ size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len) * * @param string Null terminated C "string". * @param start Start offset where to delete from. (cursor) - * @param runeWidth Reference to size_t, return number of columns for removed rune, or -1 on failure. + * @param outRuneWidth Reference to size_t, return number of columns for removed rune, or -1 on failure. * @return Number of bytes removed from buffer. */ -size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *runeWidth) +size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *outRuneWidth) { assert(string); - if (runeWidth) - *runeWidth = 0; + if (outRuneWidth) + *outRuneWidth = 0; size_t len = strlen(string), oldStart = start; if (len == 0 || len < start || !*string) @@ -151,8 +151,8 @@ size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *runeWidth) start -= _bmUtf8RunePrev(string, start); - if (runeWidth) - *runeWidth = _bmUtf8RuneWidth(string + start, oldStart - start); + if (outRuneWidth) + *outRuneWidth = _bmUtf8RuneWidth(string + start, oldStart - start); memmove(string + start, string + oldStart, len - oldStart); string[len - (oldStart - start)] = 0; @@ -167,15 +167,15 @@ size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *runeWidth) * @param start Start offset where to insert to. (cursor) * @param rune Buffer to insert to string. * @param u8len Byte length of the rune. - * @param runeWidth Reference to size_t, return number of columns for inserted rune, or -1 on failure. + * @param outRuneWidth Reference to size_t, return number of columns for inserted rune, or -1 on failure. * @return Number of bytes inserted to buffer. */ -size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *runeWidth) +size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *outRuneWidth) { assert(string); - if (runeWidth) - *runeWidth = 0; + if (outRuneWidth) + *outRuneWidth = 0; size_t len = strlen(string); if (len + u8len >= bufSize) @@ -188,8 +188,8 @@ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char memmove(str + u8len, str, len - start); memcpy(str, rune, u8len); - if (runeWidth) - *runeWidth = _bmUtf8RuneWidth(rune, u8len); + if (outRuneWidth) + *outRuneWidth = _bmUtf8RuneWidth(rune, u8len); return u8len; } @@ -200,10 +200,10 @@ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char * @param bufSize Size of the buffer. * @param start Start offset where to insert to. (cursor) * @param unicode Unicode character to insert. - * @param runeWidth Reference to size_t, return number of columns for inserted rune, or -1 on failure. + * @param outRuneWidth Reference to size_t, return number of columns for inserted rune, or -1 on failure. * @return Number of bytes inserted to buffer. */ -size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *runeWidth) +size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *outRuneWidth) { assert(string); @@ -219,7 +219,7 @@ size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int mb[0] |= (unicode >> (i * 6 - 6)); } - return _bmUtf8RuneInsert(string, bufSize, start, mb, u8len, runeWidth); + return _bmUtf8RuneInsert(string, bufSize, start, mb, u8len, outRuneWidth); } /* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From a6d0413b972580f3bbfde8750090270b0d8d463e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 20:04:06 +0300 Subject: Implement list structure, and feature for multiple selections. --- client/client.c | 8 +- lib/CMakeLists.txt | 1 + lib/bemenu.h | 56 ++++++++++++-- lib/draw/curses.c | 8 +- lib/filter.c | 28 +++---- lib/internal.h | 64 ++++++++++----- lib/list.c | 135 ++++++++++++++++++++++++++++++++ lib/menu.c | 223 ++++++++++++++++++++++++++++++++--------------------- 8 files changed, 386 insertions(+), 137 deletions(-) create mode 100644 lib/list.c (limited to 'lib/menu.c') diff --git a/client/client.c b/client/client.c index 4bdfdc6..ecaed25 100644 --- a/client/client.c +++ b/client/client.c @@ -101,10 +101,10 @@ int main(int argc, char **argv) } while ((status = bmMenuRunWithKey(menu, key, unicode)) == BM_RUN_RESULT_RUNNING); if (status == BM_RUN_RESULT_SELECTED) { - bmItem *item = bmMenuGetSelectedItem(menu); - - if (item) - printf("%s\n", bmItemGetText(item)); + unsigned int i, count; + bmItem **items = bmMenuGetSelectedItems(menu, &count); + for (i = 0; i < count; ++i) + printf("%s\n", bmItemGetText(items[i])); } bmMenuFree(menu); diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index e669ef5..f53dfc6 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -3,6 +3,7 @@ SET(BEMENU_SOURCE menu.c item.c filter.c + list.c util.c draw/curses.c ) diff --git a/lib/bemenu.h b/lib/bemenu.h index f2f0372..250ad53 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -85,6 +85,7 @@ typedef enum bmKey { BM_KEY_TAB, BM_KEY_ESCAPE, BM_KEY_RETURN, + BM_KEY_SHIFT_RETURN, BM_KEY_UNICODE, BM_KEY_LAST } bmKey; @@ -200,24 +201,52 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index); */ int bmMenuRemoveItem(bmMenu *menu, bmItem *item); + /** - * Get selected item from bmMenu instance. + * Get highlighted item from bmMenu instance. * - * @param menu bmMenu instance from where to get selected item. - * @return Selected bmItem instance, **NULL** if none selected. + * @param menu bmMenu instance from where to get highlighted item. + * @return Selected bmItem instance, **NULL** if none highlighted. */ -bmItem* bmMenuGetSelectedItem(const bmMenu *menu); +bmItem* bmMenuGetHighlightedItem(const bmMenu *menu); /** - * Get items from bmMenu instance. + * Highlight item in menu by index. * - * @warning The pointer returned by this function may be invalid after removing or adding new items. + * @param menu bmMenu instance from where to highlight item. + * @return 1 on successful highlight, 0 on failure. + */ +int bmMenuSetHighlightedIndex(bmMenu *menu, unsigned int index); + +/** + * Highlight item in menu. * - * @param menu bmMenu instance from where to get items. + * @param menu bmMenu instance from where to highlight item. + * @param item bmItem instance to highlight. + * @return 1 on successful highlight, 0 on failure. + */ +int bmMenuSetHighlighted(bmMenu *menu, bmItem *item); + +/** + * Get selected items from bmMenu instance. + * + * @param menu bmMenu instance from where to get selected items. * @param outNmemb Reference to unsigned int where total count of returned items will be stored. * @return Pointer to array of bmItem pointers. */ -bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb); +bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb); + +/** + * Set selected items to bmMenu instance. + * + * @warning The list won't be copied. + * + * @param menu bmMenu instance where items will be set. + * @param items Array of bmItem pointers to set. + * @param nmemb Total count of items in array. + * @return 1 on successful set, 0 on failure. + */ +int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int nmemb); /** * Get filtered (displayed) items from bmMenu instance. @@ -231,6 +260,17 @@ bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb); */ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb); +/** + * Get items from bmMenu instance. + * + * @warning The pointer returned by this function may be invalid after removing or adding new items. + * + * @param menu bmMenu instance from where to get items. + * @param outNmemb Reference to unsigned int where total count of returned items will be stored. + * @return Pointer to array of bmItem pointers. + */ +bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb); + /** * Set items to bmMenu instance. * Will replace all the old items on bmMenu instance. diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 0e19538..336b21d 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -120,8 +120,9 @@ static void _bmDrawCursesRender(const bmMenu *menu) unsigned int itemsCount; bmItem **items = bmMenuGetFilteredItems(menu, &itemsCount); for (i = (menu->index / (lines - 1)) * (lines - 1); i < itemsCount && cl < lines; ++i) { - int selected = (items[i] == bmMenuGetSelectedItem(menu)); - _bmDrawCursesDrawLine((selected ? 2 : 0), cl++, "%s%s", (selected ? ">> " : " "), items[i]->text); + int highlighted = (items[i] == bmMenuGetHighlightedItem(menu)); + int color = (highlighted ? 2 : (_bmMenuItemIsSelected(menu, items[i]) ? 1 : 0)); + _bmDrawCursesDrawLine(color, cl++, "%s%s", (highlighted ? ">> " : " "), items[i]->text); } curses.move(0, titleLen + menu->cursesCursor); @@ -200,6 +201,9 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) case 9: /* Tab */ return BM_KEY_TAB; + case 18: /* C-r */ + return BM_KEY_SHIFT_RETURN; + case 10: /* Return */ _bmDrawCursesEndWin(); return BM_KEY_RETURN; diff --git a/lib/filter.c b/lib/filter.c index 33126c0..4677e73 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -8,33 +8,33 @@ * * @param menu bmMenu instance to filter. * @param outNmemb unsigned int reference to filtered items outNmemb. - * @param outSelected unsigned int reference to new outSelected item index. + * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected) +bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) { assert(menu); assert(outNmemb); - assert(outSelected); - *outNmemb = *outSelected = 0; + assert(outHighlighted); + *outNmemb = *outHighlighted = 0; /* FIXME: not real dmenu like filtering at all */ - bmItem **filtered = calloc(menu->itemsCount, sizeof(bmItem*)); + bmItem **filtered = calloc(menu->items.count, sizeof(bmItem*)); if (!filtered) return NULL; unsigned int i, f; - for (f = i = 0; i < menu->itemsCount; ++i) { - bmItem *item = menu->items[i]; + for (f = i = 0; i < menu->items.count; ++i) { + bmItem *item = menu->items.list[i]; if (item->text && strstr(item->text, menu->filter)) { - if (f == 0 || item == bmMenuGetSelectedItem(menu)) - *outSelected = f; + if (f == 0 || item == bmMenuGetHighlightedItem(menu)) + *outHighlighted = f; filtered[f++] = item; } } - return _bmShrinkItemList(&filtered, menu->itemsCount, (*outNmemb = f)); + return _bmShrinkItemList(&filtered, menu->items.count, (*outNmemb = f)); } /** @@ -42,15 +42,15 @@ bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outS * * @param menu bmMenu instance to filter. * @param outNmemb unsigned int reference to filtered items outNmemb. - * @param outSelected unsigned int reference to new outSelected item index. + * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected) +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) { assert(menu); assert(outNmemb); - assert(outSelected); - *outNmemb = *outSelected = 0; + assert(outHighlighted); + *outNmemb = *outHighlighted = 0; /* FIXME: stub */ diff --git a/lib/internal.h b/lib/internal.h index 66eabbc..d0a9848 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -38,6 +38,23 @@ struct _bmRenderApi { void (*free)(void); }; +struct _bmItemList { + /** + * Items in the list. + */ + struct _bmItem **list; + + /** + * Number of items. + */ + unsigned int count; + + /** + * Number of allocated items. + */ + unsigned int allocated; +}; + /** * Internal bmMenu struct that is not exposed to public. */ @@ -48,14 +65,19 @@ struct _bmMenu { struct _bmRenderApi renderApi; /** - * All items contained in menu instance. + * Items contained in menu instance. */ - struct _bmItem **items; + struct _bmItemList items; /** * Filtered/displayed items contained in menu instance. */ - struct _bmItem **filteredItems; + struct _bmItemList filtered; + + /** + * Selected items. + */ + struct _bmItemList selection; /** * Menu instance title. @@ -79,22 +101,7 @@ struct _bmMenu { unsigned int cursesCursor; /** - * Number of items in menu instance. - */ - unsigned int itemsCount; - - /** - * Number of filtered items in menu instance. - */ - unsigned int filteredCount; - - /** - * Number of allocated items in menu instance. - */ - unsigned int allocatedCount; - - /** - * Current filtered item index in menu instance. + * Current filtered/highlighted item index in menu instance. * This index is valid for the list returned by bmMenuGetFilteredItems. */ unsigned int index; @@ -113,9 +120,24 @@ struct _bmMenu { /* draw/curses.c */ int _bmDrawCursesInit(struct _bmRenderApi *api); +/* menu.c */ +int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item); + /* filter.c */ -bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected); -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected); +bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted); +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted); + +/* list.c */ +void _bmItemListFreeList(struct _bmItemList *list); +void _bmItemListFreeItems(struct _bmItemList *list); +bmItem** _bmItemListGetItems(const struct _bmItemList *list, unsigned int *outNmemb); +int _bmItemListSetItemsNoCopy(struct _bmItemList *list, bmItem **items, unsigned int nmemb); +int _bmItemListSetItems(struct _bmItemList *list, const bmItem **items, unsigned int nmemb); +int _bmItemListGrow(struct _bmItemList *list, unsigned int step); +int _bmItemListAddItemAt(struct _bmItemList *list, bmItem *item, unsigned int index); +int _bmItemListAddItem(struct _bmItemList *list, bmItem *item); +int _bmItemListRemoveItemAt(struct _bmItemList *list, unsigned int index); +int _bmItemListRemoveItem(struct _bmItemList *list, const bmItem *item); /* util.c */ char* _bmStrdup(const char *s); diff --git a/lib/list.c b/lib/list.c new file mode 100644 index 0000000..2123818 --- /dev/null +++ b/lib/list.c @@ -0,0 +1,135 @@ +#include "internal.h" +#include +#include +#include + +void _bmItemListFreeList(struct _bmItemList *list) +{ + assert(list); + + if (list->list) + free(list->list); + + list->allocated = list->count = 0; + list->list = NULL; +} + +void _bmItemListFreeItems(struct _bmItemList *list) +{ + assert(list); + + unsigned int i; + for (i = 0; i < list->count; ++i) + bmItemFree(list->list[i]); + + _bmItemListFreeList(list); +} + +bmItem** _bmItemListGetItems(const struct _bmItemList *list, unsigned int *outNmemb) +{ + assert(list); + + if (outNmemb) + *outNmemb = list->count; + + return list->list; +} + +int _bmItemListSetItemsNoCopy(struct _bmItemList *list, bmItem **items, unsigned int nmemb) +{ + assert(list); + + _bmItemListFreeList(list); + + if (!items || nmemb == 0) { + items = NULL; + nmemb = 0; + } + + list->list = items; + list->allocated = list->count = nmemb; + return 1; +} + +int _bmItemListSetItems(struct _bmItemList *list, const bmItem **items, unsigned int nmemb) +{ + assert(list); + + if (!items || nmemb == 0) { + _bmItemListFreeItems(list); + return 1; + } + + bmItem **newItems; + if (!(newItems = calloc(sizeof(bmItem*), nmemb))) + return 0; + + memcpy(newItems, items, sizeof(bmItem*) * nmemb); + return _bmItemListSetItemsNoCopy(list, newItems, nmemb); +} + +int _bmItemListGrow(struct _bmItemList *list, unsigned int step) +{ + assert(list); + + void *tmp; + unsigned int nsize = sizeof(bmItem*) * (list->allocated + step); + + if (!list->list || !(tmp = realloc(list->list, nsize))) { + if (!(tmp = malloc(nsize))) + return 0; + + if (list->list) + memcpy(tmp, list->list, sizeof(bmItem*) * list->allocated); + } + + list->list = tmp; + list->allocated += step; + memset(&list->list[list->count], 0, sizeof(bmItem*) * (list->allocated - list->count)); + return 1; +} + +int _bmItemListAddItemAt(struct _bmItemList *list, bmItem *item, unsigned int index) +{ + assert(list); + assert(item); + + if ((!list->list || list->allocated <= list->count) && !_bmItemListGrow(list, 32)) + return 0; + + if (index + 1 != list->count) { + unsigned int i = index; + memmove(&list->list[i + 1], &list->list[i], sizeof(bmItem*) * (list->count - i)); + } + + list->list[index] = item; + list->count++; + return 1; +} + +int _bmItemListAddItem(struct _bmItemList *list, bmItem *item) +{ + assert(list); + return _bmItemListAddItemAt(list, item, list->count); +} + +int _bmItemListRemoveItemAt(struct _bmItemList *list, unsigned int index) +{ + assert(list); + + unsigned int i = index; + if (!list->list || list->count <= i) + return 0; + + memmove(&list->list[i], &list->list[i], sizeof(bmItem*) * (list->count - i)); + return 1; +} + +int _bmItemListRemoveItem(struct _bmItemList *list, const bmItem *item) +{ + unsigned int i; + for (i = 0; i < list->count && list->list[i] != item; ++i); + return _bmItemListRemoveItemAt(list, i); +} + +/* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/menu.c b/lib/menu.c index 3d41121..6274fe3 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -7,7 +7,7 @@ /** * Filter function map. */ -static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected) = { +static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) = { _bmFilterDmenu, /* BM_FILTER_DMENU */ _bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */ }; @@ -16,37 +16,22 @@ static void _bmMenuFilter(bmMenu *menu) { assert(menu); - if (menu->filteredItems) - free(menu->filteredItems); - - menu->filteredCount = 0; - menu->filteredItems = NULL; - unsigned int count, selected; bmItem **filtered = filterFunc[menu->filterMode](menu, &count, &selected); - menu->filteredItems = filtered; - menu->filteredCount = count; + _bmItemListSetItemsNoCopy(&menu->filtered, filtered, count); menu->index = selected; } -static int _bmMenuGrowItems(bmMenu *menu) +int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item) { - void *tmp; - static const unsigned int step = 32; - unsigned int nsize = sizeof(bmItem*) * (menu->allocatedCount + step); - - if (!(tmp = realloc(menu->items, nsize))) { - if (!(tmp = malloc(nsize))) - return 0; - - memcpy(tmp, menu->items, sizeof(bmItem*) * menu->allocatedCount); - } + assert(menu); + assert(item); - menu->items = tmp; - menu->allocatedCount += step; - memset(&menu->items[menu->itemsCount], 0, sizeof(bmItem*) * (menu->allocatedCount - menu->itemsCount)); - return 1; + unsigned int i, count; + bmItem **items = bmMenuGetSelectedItems(menu, &count); + for (i = 0; i < count && items[i] != item; ++i); + return (i < count); } /** @@ -97,8 +82,8 @@ void bmMenuFree(bmMenu *menu) if (menu->title) free(menu->title); - if (menu->filteredItems) - free(menu->filteredItems); + if (menu->filtered.list) + free(menu->filtered.list); bmMenuFreeItems(menu); free(menu); @@ -112,14 +97,9 @@ void bmMenuFree(bmMenu *menu) void bmMenuFreeItems(bmMenu *menu) { assert(menu); - - unsigned int i; - for (i = 0; i < menu->itemsCount; ++i) - bmItemFree(menu->items[i]); - - free(menu->items); - menu->allocatedCount = menu->itemsCount = 0; - menu->items = NULL; + _bmItemListFreeList(&menu->selection); + _bmItemListFreeList(&menu->filtered); + _bmItemListFreeItems(&menu->items); } /** @@ -195,19 +175,7 @@ const char* bmMenuGetTitle(const bmMenu *menu) int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) { assert(menu); - assert(item); - - if (menu->itemsCount >= menu->allocatedCount && !_bmMenuGrowItems(menu)) - return 0; - - if (index + 1 != menu->itemsCount) { - unsigned int i = index; - memmove(&menu->items[i + 1], &menu->items[i], sizeof(bmItem*) * (menu->itemsCount - i)); - } - - menu->items[index] = item; - menu->itemsCount++; - return 1; + return _bmItemListAddItemAt(&menu->items, item, index);; } /** @@ -219,7 +187,7 @@ int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) */ int bmMenuAddItem(bmMenu *menu, bmItem *item) { - return bmMenuAddItemAt(menu, item, menu->itemsCount); + return _bmItemListAddItem(&menu->items, item); } /** @@ -235,12 +203,18 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) { assert(menu); - unsigned int i = index; - if (i >= menu->itemsCount) + if (!menu->items.list || menu->items.count <= index) return 0; - memmove(&menu->items[i], &menu->items[i], sizeof(bmItem*) * (menu->itemsCount - i)); - return 1; + bmItem *item = menu->items.list[index]; + int ret = _bmItemListRemoveItemAt(&menu->items, index); + + if (ret) { + _bmItemListRemoveItem(&menu->selection, item); + _bmItemListRemoveItem(&menu->filtered, item); + } + + return ret; } /** @@ -255,20 +229,24 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) int bmMenuRemoveItem(bmMenu *menu, bmItem *item) { assert(menu); - assert(item); - unsigned int i; - for (i = 0; i < menu->itemsCount && menu->items[i] != item; ++i); - return bmMenuRemoveItemAt(menu, i); + int ret = _bmItemListRemoveItem(&menu->items, item); + + if (ret) { + _bmItemListRemoveItem(&menu->selection, item); + _bmItemListRemoveItem(&menu->filtered, item); + } + + return ret; } /** - * Get selected item from bmMenu instance. + * Get highlighted item from bmMenu instance. * - * @param menu bmMenu instance from where to get selected item. - * @return Selected bmItem instance, **NULL** if none selected. + * @param menu bmMenu instance from where to get highlighted item. + * @return Selected bmItem instance, **NULL** if none highlighted. */ -bmItem* bmMenuGetSelectedItem(const bmMenu *menu) +bmItem* bmMenuGetHighlightedItem(const bmMenu *menu) { assert(menu); @@ -282,22 +260,70 @@ bmItem* bmMenuGetSelectedItem(const bmMenu *menu) } /** - * Get items from bmMenu instance. + * Highlight item in menu by index. * - * @warning The pointer returned by this function may be invalid after removing or adding new items. + * @param menu bmMenu instance from where to highlight item. + * @return 1 on successful highlight, 0 on failure. + */ +int bmMenuSetHighlightedIndex(bmMenu *menu, unsigned int index) +{ + assert(menu); + unsigned int itemsCount = (menu->filtered.list ? menu->filtered.count : menu->items.count); + + if (itemsCount <= index) + return 0; + + return (menu->index = index); +} + +/** + * Highlight item in menu. * - * @param menu bmMenu instance from where to get items. + * @param menu bmMenu instance from where to highlight item. + * @param item bmItem instance to highlight. + * @return 1 on successful highlight, 0 on failure. + */ +int bmMenuSetHighlighted(bmMenu *menu, bmItem *item) +{ + assert(menu); + + unsigned int i, itemsCount; + bmItem **items = bmMenuGetFilteredItems(menu, &itemsCount); + for (i = 0; i < itemsCount && items[i] != item; ++i); + + if (itemsCount <= i) + return 0; + + return (menu->index = i); +} + +/** + * Get selected items from bmMenu instance. + * + * @param menu bmMenu instance from where to get selected items. * @param outNmemb Reference to unsigned int where total count of returned items will be stored. * @return Pointer to array of bmItem pointers. */ -bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb) +bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); + return _bmItemListGetItems(&menu->selection, outNmemb); +} - if (outNmemb) - *outNmemb = menu->itemsCount; - - return menu->items; +/** + * Set selected items to bmMenu instance. + * + * @warning The list won't be copied. + * + * @param menu bmMenu instance where items will be set. + * @param items Array of bmItem pointers to set. + * @param nmemb Total count of items in array. + * @return 1 on successful set, 0 on failure. + */ +int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int nmemb) +{ + assert(menu); + return _bmItemListSetItemsNoCopy(&menu->selection, items, nmemb); } /** @@ -314,10 +340,25 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); - if (outNmemb) - *outNmemb = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); + if (menu->filtered.list) + return _bmItemListGetItems(&menu->filtered, outNmemb); - return (menu->filteredItems ? menu->filteredItems : menu->items); + return _bmItemListGetItems(&menu->items, outNmemb); +} + +/** + * Get items from bmMenu instance. + * + * @warning The pointer returned by this function may be invalid after removing or adding new items. + * + * @param menu bmMenu instance from where to get items. + * @param outNmemb Reference to unsigned int where total count of returned items will be stored. + * @return Pointer to array of bmItem pointers. + */ +bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb) +{ + assert(menu); + return _bmItemListGetItems(&menu->items, outNmemb); } /** @@ -335,21 +376,14 @@ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) { assert(menu); - if (!items || nmemb == 0) { - bmMenuFreeItems(menu); - return 1; - } + int ret = _bmItemListSetItems(&menu->items, items, nmemb); - bmItem **newItems; - if (!(newItems = calloc(sizeof(bmItem*), nmemb))) - return 0; - - memcpy(newItems, items, sizeof(bmItem*) * nmemb); - bmMenuFreeItems(menu); + if (ret) { + _bmItemListFreeList(&menu->selection); + _bmItemListFreeList(&menu->filtered); + } - menu->items = newItems; - menu->allocatedCount = menu->itemsCount = nmemb; - return 1; + return ret; } /** @@ -400,7 +434,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) { assert(menu); char *oldFilter = _bmStrdup(menu->filter); - unsigned int itemsCount = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); + unsigned int itemsCount = (menu->filtered.list ? menu->filtered.count : menu->items.count); switch (key) { case BM_KEY_LEFT: @@ -502,9 +536,9 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) case BM_KEY_TAB: { - bmItem *selected = bmMenuGetSelectedItem(menu); - if (selected && bmItemGetText(selected)) { - const char *text = bmItemGetText(selected); + bmItem *highlighted = bmMenuGetHighlightedItem(menu); + if (highlighted && bmItemGetText(highlighted)) { + const char *text = bmItemGetText(highlighted); size_t len = strlen(text); if (len > sizeof(menu->filter) - 1) @@ -518,6 +552,19 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) } break; + case BM_KEY_SHIFT_RETURN: + case BM_KEY_RETURN: + { + bmItem *highlighted = bmMenuGetHighlightedItem(menu); + if (highlighted && !_bmMenuItemIsSelected(menu, highlighted)) + _bmItemListAddItem(&menu->selection, highlighted); + } + break; + + case BM_KEY_ESCAPE: + _bmItemListFreeList(&menu->selection); + break; + default: break; } -- cgit v1.2.3-70-g09d2 From 702d808b285931850e66900bd6c7c7319ae6d73c Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 20:09:34 +0300 Subject: Make it possible to set and get userdata pointers. --- lib/bemenu.h | 34 ++++++++++++++++++++++++++++++++++ lib/internal.h | 12 ++++++++++++ lib/item.c | 25 +++++++++++++++++++++++++ lib/menu.c | 25 +++++++++++++++++++++++++ 4 files changed, 96 insertions(+) (limited to 'lib/menu.c') diff --git a/lib/bemenu.h b/lib/bemenu.h index 250ad53..65625b3 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -122,6 +122,23 @@ void bmMenuFreeItems(bmMenu *menu); * @name Menu Properties * @{ */ +/** + * Set userdata pointer to bmMenu instance. + * Userdata will be carried unmodified by the instance. + * + * @param menu bmMenu instance where to set userdata pointer. + * @param userdata Pointer to userdata. + */ +void bmMenuSetUserdata(bmMenu *menu, void *userdata); + +/** + * Get userdata pointer from bmMenu instance. + * + * @param menu bmMenu instance which userdata pointer to get. + * @return Pointer for unmodified userdata. + */ +void* bmMenuGetUserdata(bmMenu *menu); + /** * Set active filter mode to bmMenu instance. * @@ -351,6 +368,23 @@ void bmItemFree(bmItem *item); * @name Item Properties * @{ */ +/** + * Set userdata pointer to bmItem instance. + * Userdata will be carried unmodified by the instance. + * + * @param item bmItem instance where to set userdata pointer. + * @param userdata Pointer to userdata. + */ +void bmItemSetUserdata(bmItem *item, void *userdata); + +/** + * Get userdata pointer from bmItem instance. + * + * @param item bmItem instance which userdata pointer to get. + * @return Pointer for unmodified userdata. + */ +void* bmItemGetUserdata(bmItem *item); + /** * Set text to bmItem instance. * diff --git a/lib/internal.h b/lib/internal.h index d0a9848..790b4ed 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -9,6 +9,12 @@ * Represents a single item in menu. */ struct _bmItem { + /** + * Userdata pointer. + * This pointer will be passed around with the item untouched. + */ + void *userdata; + /** * Primary text shown on item as null terminated C "string". * Matching will be done against this text as well. @@ -59,6 +65,12 @@ struct _bmItemList { * Internal bmMenu struct that is not exposed to public. */ struct _bmMenu { + /** + * Userdata pointer. + * This pointer will be passed around with the menu untouched. + */ + void *userdata; + /** * Underlying renderer access. */ diff --git a/lib/item.c b/lib/item.c index e6ccc7a..f2a879a 100644 --- a/lib/item.c +++ b/lib/item.c @@ -35,6 +35,31 @@ void bmItemFree(bmItem *item) free(item); } +/** + * Set userdata pointer to bmItem instance. + * Userdata will be carried unmodified by the instance. + * + * @param item bmItem instance where to set userdata pointer. + * @param userdata Pointer to userdata. + */ +void bmItemSetUserdata(bmItem *item, void *userdata) +{ + assert(item); + item->userdata = userdata; +} + +/** + * Get userdata pointer from bmItem instance. + * + * @param item bmItem instance which userdata pointer to get. + * @return Pointer for unmodified userdata. + */ +void* bmItemGetUserdata(bmItem *item) +{ + assert(item); + return item->userdata; +} + /** * Set text to bmItem instance. * diff --git a/lib/menu.c b/lib/menu.c index 6274fe3..a011194 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -102,6 +102,31 @@ void bmMenuFreeItems(bmMenu *menu) _bmItemListFreeItems(&menu->items); } +/** + * Set userdata pointer to bmMenu instance. + * Userdata will be carried unmodified by the instance. + * + * @param menu bmMenu instance where to set userdata pointer. + * @param userdata Pointer to userdata. + */ +void bmMenuSetUserdata(bmMenu *menu, void *userdata) +{ + assert(menu); + menu->userdata = userdata; +} + +/** + * Get userdata pointer from bmMenu instance. + * + * @param menu bmMenu instance which userdata pointer to get. + * @return Pointer for unmodified userdata. + */ +void* bmMenuGetUserdata(bmMenu *menu) +{ + assert(menu); + return menu->userdata; +} + /** * Set active filter mode to bmMenu instance. * -- cgit v1.2.3-70-g09d2 From 9525c77f553c2e188f6f99d3b6c01d5ec671a76e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 20:11:41 +0300 Subject: Setter first, then getter. --- lib/bemenu.h | 55 +++++++++++++++++----------------- lib/menu.c | 98 ++++++++++++++++++++++++++++++------------------------------ 2 files changed, 76 insertions(+), 77 deletions(-) (limited to 'lib/menu.c') diff --git a/lib/bemenu.h b/lib/bemenu.h index 65625b3..7d96d2c 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -218,15 +218,6 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index); */ int bmMenuRemoveItem(bmMenu *menu, bmItem *item); - -/** - * Get highlighted item from bmMenu instance. - * - * @param menu bmMenu instance from where to get highlighted item. - * @return Selected bmItem instance, **NULL** if none highlighted. - */ -bmItem* bmMenuGetHighlightedItem(const bmMenu *menu); - /** * Highlight item in menu by index. * @@ -245,13 +236,12 @@ int bmMenuSetHighlightedIndex(bmMenu *menu, unsigned int index); int bmMenuSetHighlighted(bmMenu *menu, bmItem *item); /** - * Get selected items from bmMenu instance. + * Get highlighted item from bmMenu instance. * - * @param menu bmMenu instance from where to get selected items. - * @param outNmemb Reference to unsigned int where total count of returned items will be stored. - * @return Pointer to array of bmItem pointers. + * @param menu bmMenu instance from where to get highlighted item. + * @return Selected bmItem instance, **NULL** if none highlighted. */ -bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb); +bmItem* bmMenuGetHighlightedItem(const bmMenu *menu); /** * Set selected items to bmMenu instance. @@ -266,16 +256,26 @@ bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb); int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int nmemb); /** - * Get filtered (displayed) items from bmMenu instance. - * - * @warning The pointer returned by this function _will_ be invalid when menu internally filters its list again. - * Do not store this pointer. + * Get selected items from bmMenu instance. * - * @param menu bmMenu instance from where to get filtered items. + * @param menu bmMenu instance from where to get selected items. * @param outNmemb Reference to unsigned int where total count of returned items will be stored. * @return Pointer to array of bmItem pointers. */ -bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb); +bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb); + +/** + * Set items to bmMenu instance. + * Will replace all the old items on bmMenu instance. + * + * If items is **NULL**, or nmemb is zero, all items will be freed from the menu. + * + * @param menu bmMenu instance where items will be set. + * @param items Array of bmItem pointers to set. + * @param nmemb Total count of items in array. + * @return 1 on successful set, 0 on failure. + */ +int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb); /** * Get items from bmMenu instance. @@ -289,17 +289,16 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb); bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb); /** - * Set items to bmMenu instance. - * Will replace all the old items on bmMenu instance. + * Get filtered (displayed) items from bmMenu instance. * - * If items is **NULL**, or nmemb is zero, all items will be freed from the menu. + * @warning The pointer returned by this function _will_ be invalid when menu internally filters its list again. + * Do not store this pointer. * - * @param menu bmMenu instance where items will be set. - * @param items Array of bmItem pointers to set. - * @param nmemb Total count of items in array. - * @return 1 on successful set, 0 on failure. + * @param menu bmMenu instance from where to get filtered items. + * @param outNmemb Reference to unsigned int where total count of returned items will be stored. + * @return Pointer to array of bmItem pointers. */ -int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb); +bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb); /** @} Menu Items */ diff --git a/lib/menu.c b/lib/menu.c index a011194..f0a8149 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -265,25 +265,6 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) return ret; } -/** - * Get highlighted item from bmMenu instance. - * - * @param menu bmMenu instance from where to get highlighted item. - * @return Selected bmItem instance, **NULL** if none highlighted. - */ -bmItem* bmMenuGetHighlightedItem(const bmMenu *menu) -{ - assert(menu); - - unsigned int count; - bmItem **items = bmMenuGetFilteredItems(menu, &count); - - if (!items || count <= menu->index) - return NULL; - - return items[menu->index]; -} - /** * Highlight item in menu by index. * @@ -323,16 +304,22 @@ int bmMenuSetHighlighted(bmMenu *menu, bmItem *item) } /** - * Get selected items from bmMenu instance. + * Get highlighted item from bmMenu instance. * - * @param menu bmMenu instance from where to get selected items. - * @param outNmemb Reference to unsigned int where total count of returned items will be stored. - * @return Pointer to array of bmItem pointers. + * @param menu bmMenu instance from where to get highlighted item. + * @return Selected bmItem instance, **NULL** if none highlighted. */ -bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb) +bmItem* bmMenuGetHighlightedItem(const bmMenu *menu) { assert(menu); - return _bmItemListGetItems(&menu->selection, outNmemb); + + unsigned int count; + bmItem **items = bmMenuGetFilteredItems(menu, &count); + + if (!items || count <= menu->index) + return NULL; + + return items[menu->index]; } /** @@ -352,23 +339,41 @@ int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int nmemb) } /** - * Get filtered (displayed) items from bmMenu instance. - * - * @warning The pointer returned by this function _will_ be invalid when menu internally filters its list again. - * Do not store this pointer. + * Get selected items from bmMenu instance. * - * @param menu bmMenu instance from where to get filtered items. + * @param menu bmMenu instance from where to get selected items. * @param outNmemb Reference to unsigned int where total count of returned items will be stored. * @return Pointer to array of bmItem pointers. */ -bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb) +bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); + return _bmItemListGetItems(&menu->selection, outNmemb); +} - if (menu->filtered.list) - return _bmItemListGetItems(&menu->filtered, outNmemb); +/** + * Set items to bmMenu instance. + * Will replace all the old items on bmMenu instance. + * + * If items is **NULL**, or nmemb is zero, all items will be freed from the menu. + * + * @param menu bmMenu instance where items will be set. + * @param items Array of bmItem pointers to set. + * @param nmemb Total count of items in array. + * @return 1 on successful set, 0 on failure. + */ +int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) +{ + assert(menu); - return _bmItemListGetItems(&menu->items, outNmemb); + int ret = _bmItemListSetItems(&menu->items, items, nmemb); + + if (ret) { + _bmItemListFreeList(&menu->selection); + _bmItemListFreeList(&menu->filtered); + } + + return ret; } /** @@ -387,28 +392,23 @@ bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb) } /** - * Set items to bmMenu instance. - * Will replace all the old items on bmMenu instance. + * Get filtered (displayed) items from bmMenu instance. * - * If items is **NULL**, or nmemb is zero, all items will be freed from the menu. + * @warning The pointer returned by this function _will_ be invalid when menu internally filters its list again. + * Do not store this pointer. * - * @param menu bmMenu instance where items will be set. - * @param items Array of bmItem pointers to set. - * @param nmemb Total count of items in array. - * @return 1 on successful set, 0 on failure. + * @param menu bmMenu instance from where to get filtered items. + * @param outNmemb Reference to unsigned int where total count of returned items will be stored. + * @return Pointer to array of bmItem pointers. */ -int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) +bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); - int ret = _bmItemListSetItems(&menu->items, items, nmemb); - - if (ret) { - _bmItemListFreeList(&menu->selection); - _bmItemListFreeList(&menu->filtered); - } + if (menu->filtered.list) + return _bmItemListGetItems(&menu->filtered, outNmemb); - return ret; + return _bmItemListGetItems(&menu->items, outNmemb); } /** -- cgit v1.2.3-70-g09d2 From 49fc7cd08c646d44e63749dc4e0c38dcb2d00a50 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 20:12:42 +0300 Subject: Include index parameter in documentation. --- lib/bemenu.h | 1 + lib/menu.c | 1 + 2 files changed, 2 insertions(+) (limited to 'lib/menu.c') diff --git a/lib/bemenu.h b/lib/bemenu.h index 7d96d2c..35a94e5 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -222,6 +222,7 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item); * Highlight item in menu by index. * * @param menu bmMenu instance from where to highlight item. + * @param index Index of item to highlight. * @return 1 on successful highlight, 0 on failure. */ int bmMenuSetHighlightedIndex(bmMenu *menu, unsigned int index); diff --git a/lib/menu.c b/lib/menu.c index f0a8149..5f57ae8 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -269,6 +269,7 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) * Highlight item in menu by index. * * @param menu bmMenu instance from where to highlight item. + * @param index Index of item to highlight. * @return 1 on successful highlight, 0 on failure. */ int bmMenuSetHighlightedIndex(bmMenu *menu, unsigned int index) -- cgit v1.2.3-70-g09d2 From 05212f5e44571945b40f72ae70ec7012af85fe75 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 20:19:15 +0300 Subject: Improve documentation. --- lib/bemenu.h | 6 +++++- lib/menu.c | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'lib/menu.c') diff --git a/lib/bemenu.h b/lib/bemenu.h index 35a94e5..4534966 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -239,6 +239,8 @@ int bmMenuSetHighlighted(bmMenu *menu, bmItem *item); /** * Get highlighted item from bmMenu instance. * + * @warning The pointer returned by this function may be invalid after items change. + * * @param menu bmMenu instance from where to get highlighted item. * @return Selected bmItem instance, **NULL** if none highlighted. */ @@ -247,7 +249,7 @@ bmItem* bmMenuGetHighlightedItem(const bmMenu *menu); /** * Set selected items to bmMenu instance. * - * @warning The list won't be copied. + * @warning The list won't be copied, do not free it. * * @param menu bmMenu instance where items will be set. * @param items Array of bmItem pointers to set. @@ -259,6 +261,8 @@ int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int nmemb); /** * Get selected items from bmMenu instance. * + * @warning The pointer returned by this function may be invalid after selection or items change. + * * @param menu bmMenu instance from where to get selected items. * @param outNmemb Reference to unsigned int where total count of returned items will be stored. * @return Pointer to array of bmItem pointers. diff --git a/lib/menu.c b/lib/menu.c index 5f57ae8..bf26c51 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -307,6 +307,8 @@ int bmMenuSetHighlighted(bmMenu *menu, bmItem *item) /** * Get highlighted item from bmMenu instance. * + * @warning The pointer returned by this function may be invalid after items change. + * * @param menu bmMenu instance from where to get highlighted item. * @return Selected bmItem instance, **NULL** if none highlighted. */ @@ -326,7 +328,7 @@ bmItem* bmMenuGetHighlightedItem(const bmMenu *menu) /** * Set selected items to bmMenu instance. * - * @warning The list won't be copied. + * @warning The list won't be copied, do not free it. * * @param menu bmMenu instance where items will be set. * @param items Array of bmItem pointers to set. @@ -342,6 +344,8 @@ int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int nmemb) /** * Get selected items from bmMenu instance. * + * @warning The pointer returned by this function may be invalid after selection or items change. + * * @param menu bmMenu instance from where to get selected items. * @param outNmemb Reference to unsigned int where total count of returned items will be stored. * @return Pointer to array of bmItem pointers. -- cgit v1.2.3-70-g09d2 From 45502a2fd994fdb10b77e4ac79c5f9fd8a8399e8 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 22:02:47 +0300 Subject: Proper filtering functions. --- lib/filter.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++--------- lib/internal.h | 2 + lib/menu.c | 37 ++++++++++++---- lib/util.c | 60 ++++++++++++++++++-------- 4 files changed, 188 insertions(+), 44 deletions(-) (limited to 'lib/menu.c') diff --git a/lib/filter.c b/lib/filter.c index 4677e73..7e87b11 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -4,37 +4,139 @@ #include /** - * Filter that mimics the vanilla dmenu filtering. + * Shrink bmItem** list pointer. + * + * Useful helper function for filter functions. + * + * @param list Pointer to pointer to list of bmItem pointers. + * @param osize Current size of the list. + * @param nsize New size the list will be shrinked to. + * @return Pointer to list of bmItem pointers. + */ +static bmItem** _bmFilterShrinkList(bmItem ***inOutList, size_t osize, size_t nsize) +{ + assert(inOutList); + + if (nsize == 0) { + free(*inOutList); + return (*inOutList = NULL); + } + + if (nsize >= osize) + return *inOutList; + + void *tmp = malloc(sizeof(bmItem*) * nsize); + if (!tmp) + return *inOutList; + + memcpy(tmp, *inOutList, sizeof(bmItem*) * nsize); + free(*inOutList); + return (*inOutList = tmp); +} + +/** + * Text filter tokenizer helper. + * + * @param menu bmMenu instance which filter to tokenize. + * @param outTokv char pointer reference to list of tokens, this should be freed after use. + * @param outTokc unsigned int reference to number of tokens. + * @return Pointer to buffer that contains tokenized string, this should be freed after use. + */ +static char* _bmFilterTokenize(bmMenu *menu, char ***outTokv, unsigned int *outTokc) +{ + assert(menu); + assert(outTokv); + assert(outTokc); + *outTokv = NULL; + *outTokc = 0; + + char **tokv = NULL, *buffer = NULL; + if (!(buffer = _bmStrdup(menu->filter))) + goto fail; + + char *s, **tmp = NULL; + unsigned int tokc = 0, tokn = 0; + for (s = strtok(buffer, " "); s; tmp[tokc - 1] = s, s = strtok(NULL, " "), tokv = tmp) + if (++tokc > tokn && !(tmp = realloc(tmp, ++tokn * sizeof(char*)))) + goto fail; + + *outTokv = tmp; + *outTokc = tokc; + return buffer; + +fail: + if (buffer) + free(buffer); + if (tokv) + free(tokv); + return NULL; +} + +/** + * Dmenu filterer that accepts substring function. * * @param menu bmMenu instance to filter. + * @param fstrstr Substring function used to match items. * @param outNmemb unsigned int reference to filtered items outNmemb. * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const char *b), unsigned int *outNmemb, unsigned int *outHighlighted) { assert(menu); assert(outNmemb); assert(outHighlighted); *outNmemb = *outHighlighted = 0; - /* FIXME: not real dmenu like filtering at all */ + unsigned int itemsCount; + bmItem **items = bmMenuGetItems(menu, &itemsCount); - bmItem **filtered = calloc(menu->items.count, sizeof(bmItem*)); + bmItem **filtered = calloc(itemsCount, sizeof(bmItem*)); if (!filtered) return NULL; + char **tokv; + unsigned int tokc; + char *buffer = _bmFilterTokenize(menu, &tokv, &tokc); + unsigned int i, f; - for (f = i = 0; i < menu->items.count; ++i) { - bmItem *item = menu->items.list[i]; - if (item->text && strstr(item->text, menu->filter)) { - if (f == 0 || item == bmMenuGetHighlightedItem(menu)) - *outHighlighted = f; - filtered[f++] = item; + for (f = i = 0; i < itemsCount; ++i) { + bmItem *item = items[i]; + if (!item->text && tokc != 0) + continue; + + if (tokc && item->text) { + unsigned int t; + for (t = 0; t < tokc && fstrstr(item->text, tokv[t]); ++t); + if (t < tokc) + continue; } + + if (f == 0 || item == bmMenuGetHighlightedItem(menu)) + *outHighlighted = f; + filtered[f++] = item; } - return _bmShrinkItemList(&filtered, menu->items.count, (*outNmemb = f)); + if (buffer) + free(buffer); + + if (tokv) + free(tokv); + + return _bmFilterShrinkList(&filtered, menu->items.count, (*outNmemb = f)); +} + +/** + * Filter that mimics the vanilla dmenu filtering. + * + * @param menu bmMenu instance to filter. + * @param outNmemb unsigned int reference to filtered items outNmemb. + * @param outHighlighted unsigned int reference to new outHighlighted item index. + * @return Pointer to array of bmItem pointers. + */ +bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) +{ + return _bmFilterDmenuFun(menu, strstr, outNmemb, outHighlighted); } /** @@ -47,14 +149,7 @@ bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outH */ bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) { - assert(menu); - assert(outNmemb); - assert(outHighlighted); - *outNmemb = *outHighlighted = 0; - - /* FIXME: stub */ - - return NULL; + return _bmFilterDmenuFun(menu, _bmStrupstr, outNmemb, outHighlighted); } /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/internal.h b/lib/internal.h index 790b4ed..043c906 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -153,6 +153,8 @@ int _bmItemListRemoveItem(struct _bmItemList *list, const bmItem *item); /* util.c */ char* _bmStrdup(const char *s); +int _bmStrupcmp(const char *hay, const char *needle); +char* _bmStrupstr(const char *hay, const char *needle); bmItem** _bmShrinkItemList(bmItem ***inOutList, size_t osize, size_t nsize); int _bmUtf8StringScreenWidth(const char *string); size_t _bmUtf8RuneNext(const char *string, size_t start); diff --git a/lib/menu.c b/lib/menu.c index bf26c51..e9cf647 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -16,6 +16,11 @@ static void _bmMenuFilter(bmMenu *menu) { assert(menu); + if (!menu->items.list || menu->items.count <= 0) { + _bmItemListFreeList(&menu->filtered); + return; + } + unsigned int count, selected; bmItem **filtered = filterFunc[menu->filterMode](menu, &count, &selected); @@ -82,9 +87,6 @@ void bmMenuFree(bmMenu *menu) if (menu->title) free(menu->title); - if (menu->filtered.list) - free(menu->filtered.list); - bmMenuFreeItems(menu); free(menu); } @@ -200,7 +202,13 @@ const char* bmMenuGetTitle(const bmMenu *menu) int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) { assert(menu); - return _bmItemListAddItemAt(&menu->items, item, index);; + + int ret = _bmItemListAddItemAt(&menu->items, item, index); + + if (ret) + _bmMenuFilter(menu); + + return ret; } /** @@ -212,7 +220,12 @@ int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) */ int bmMenuAddItem(bmMenu *menu, bmItem *item) { - return _bmItemListAddItem(&menu->items, item); + int ret = _bmItemListAddItem(&menu->items, item); + + if (ret) + _bmMenuFilter(menu); + + return ret; } /** @@ -237,6 +250,7 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) if (ret) { _bmItemListRemoveItem(&menu->selection, item); _bmItemListRemoveItem(&menu->filtered, item); + _bmMenuFilter(menu); } return ret; @@ -260,6 +274,7 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) if (ret) { _bmItemListRemoveItem(&menu->selection, item); _bmItemListRemoveItem(&menu->filtered, item); + _bmMenuFilter(menu); } return ret; @@ -275,7 +290,9 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) int bmMenuSetHighlightedIndex(bmMenu *menu, unsigned int index) { assert(menu); - unsigned int itemsCount = (menu->filtered.list ? menu->filtered.count : menu->items.count); + + unsigned int itemsCount; + bmMenuGetFilteredItems(menu, &itemsCount); if (itemsCount <= index) return 0; @@ -376,6 +393,7 @@ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) if (ret) { _bmItemListFreeList(&menu->selection); _bmItemListFreeList(&menu->filtered); + _bmMenuFilter(menu); } return ret; @@ -410,7 +428,7 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); - if (menu->filtered.list) + if (strlen(menu->filter)) return _bmItemListGetItems(&menu->filtered, outNmemb); return _bmItemListGetItems(&menu->items, outNmemb); @@ -463,8 +481,11 @@ bmKey bmMenuGetKey(bmMenu *menu, unsigned int *outUnicode) bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) { assert(menu); + + unsigned int itemsCount; + bmMenuGetFilteredItems(menu, &itemsCount); + char *oldFilter = _bmStrdup(menu->filter); - unsigned int itemsCount = (menu->filtered.list ? menu->filtered.count : menu->items.count); switch (key) { case BM_KEY_LEFT: diff --git a/lib/util.c b/lib/util.c index 5eb980d..06c89cf 100644 --- a/lib/util.c +++ b/lib/util.c @@ -28,30 +28,56 @@ char* _bmStrdup(const char *string) } /** - * Shrink bmItem** list pointer. + * Portable case-insensitive strcmp. * - * Useful helper function for filter functions. + * @param hay C "string" to match against. + * @param needle C "string" to match. + */ +int _bmStrupcmp(const char *hay, const char *needle) +{ + size_t i, len; + + if ((len = strlen(hay)) != strlen(needle)) + return 1; + + for (i = 0; i != len; ++i) + if (toupper(hay[i]) != toupper(needle[i])) + return 1; + + return 0; +} + +/** + * Portable case-insensitive strstr. * - * @param list Pointer to pointer to list of bmItem pointers. - * @param osize Current size of the list. - * @param nsize New size the list will be shrinked to. - * @return Pointer to list of bmItem pointers. + * @param hay C "string" to substring against. + * @param needle C "string" to substring. */ -bmItem** _bmShrinkItemList(bmItem ***inOutList, size_t osize, size_t nsize) +char* _bmStrupstr(const char *hay, const char *needle) { - assert(inOutList); + size_t i, r = 0, p = 0, len, len2; - if (nsize >= osize) - return *inOutList; + if (!_bmStrupcmp(hay, needle)) + return (char*)hay; - void *tmp = malloc(sizeof(bmItem*) * nsize); - if (!tmp) - return *inOutList; + if ((len = strlen(hay)) < (len2 = strlen(needle))) + return NULL; + + for (i = 0; i != len; ++i) { + if (p == len2) + return (char*)hay + r; + + if (toupper(hay[i]) == toupper(needle[p++])) { + if (!r) + r = i; + } else { + if (r) + i = r; + r = p = 0; + } + } - memcpy(tmp, *inOutList, sizeof(bmItem*) * nsize); - free(*inOutList); - *inOutList = tmp; - return *inOutList; + return (p == len2 ? (char*)hay + r : NULL); } /** -- cgit v1.2.3-70-g09d2 From 45e21171593050aedc92b1da44d0441e4a734a1b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 22:15:01 +0300 Subject: Don't run filtering if there is no filter. --- lib/menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/menu.c') diff --git a/lib/menu.c b/lib/menu.c index e9cf647..acf9ffc 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -16,7 +16,7 @@ static void _bmMenuFilter(bmMenu *menu) { assert(menu); - if (!menu->items.list || menu->items.count <= 0) { + if (!strlen(menu->filter) || !menu->items.list || menu->items.count <= 0) { _bmItemListFreeList(&menu->filtered); return; } -- cgit v1.2.3-70-g09d2 From 71beb7583f6cba2eb8070d5a05dcdf05b54f52bf Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 23:05:13 +0300 Subject: Make it possible filter manually, and optimized filtering. --- lib/bemenu.h | 11 ++++++ lib/filter.c | 27 +++++++++++---- lib/internal.h | 9 +++-- lib/menu.c | 103 ++++++++++++++++++++++++++++++++------------------------- 4 files changed, 96 insertions(+), 54 deletions(-) (limited to 'lib/menu.c') diff --git a/lib/bemenu.h b/lib/bemenu.h index 4534966..6348693 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -318,6 +318,17 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb); */ void bmMenuRender(const bmMenu *menu); +/** + * Trigger filtering of menu manually. + * This is useful when adding new items and want to dynamically see them filtered. + * + * Do note that filtering might be heavy, so you should only call it after batch manipulation of items. + * Not after manipulation of each single item. + * + * @param menu bmMenu instance which to filter. + */ +void bmMenuFilter(bmMenu *menu); + /** * Poll key and unicode from underlying UI toolkit. * diff --git a/lib/filter.c b/lib/filter.c index 7e87b11..c8cbfff 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -76,12 +76,13 @@ fail: * Dmenu filterer that accepts substring function. * * @param menu bmMenu instance to filter. + * @param addition This will be 1, if filter is same as previous filter with something appended. * @param fstrstr Substring function used to match items. * @param outNmemb unsigned int reference to filtered items outNmemb. * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const char *b), unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const char *a, const char *b), unsigned int *outNmemb, unsigned int *outHighlighted) { assert(menu); assert(outNmemb); @@ -89,7 +90,13 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const c *outNmemb = *outHighlighted = 0; unsigned int itemsCount; - bmItem **items = bmMenuGetItems(menu, &itemsCount); + bmItem **items; + + if (addition) { + items = bmMenuGetFilteredItems(menu, &itemsCount); + } else { + items = bmMenuGetItems(menu, &itemsCount); + } bmItem **filtered = calloc(itemsCount, sizeof(bmItem*)); if (!filtered) @@ -99,6 +106,7 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const c unsigned int tokc; char *buffer = _bmFilterTokenize(menu, &tokv, &tokc); + char found = 0; unsigned int i, f; for (f = i = 0; i < itemsCount; ++i) { bmItem *item = items[i]; @@ -112,8 +120,11 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const c continue; } - if (f == 0 || item == bmMenuGetHighlightedItem(menu)) + if (!found && item == bmMenuGetHighlightedItem(menu)) { *outHighlighted = f; + found = 1; + } + filtered[f++] = item; } @@ -130,26 +141,28 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const c * Filter that mimics the vanilla dmenu filtering. * * @param menu bmMenu instance to filter. + * @param addition This will be 1, if filter is same as previous filter with something appended. * @param outNmemb unsigned int reference to filtered items outNmemb. * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) { - return _bmFilterDmenuFun(menu, strstr, outNmemb, outHighlighted); + return _bmFilterDmenuFun(menu, addition, strstr, outNmemb, outHighlighted); } /** * Filter that mimics the vanilla case-insensitive dmenu filtering. * * @param menu bmMenu instance to filter. + * @param addition This will be 1, if filter is same as previous filter with something appended. * @param outNmemb unsigned int reference to filtered items outNmemb. * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) { - return _bmFilterDmenuFun(menu, _bmStrupstr, outNmemb, outHighlighted); + return _bmFilterDmenuFun(menu, addition, _bmStrupstr, outNmemb, outHighlighted); } /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/internal.h b/lib/internal.h index 043c906..985d366 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -102,6 +102,11 @@ struct _bmMenu { */ char filter[1024]; + /** + * Used as optimization. + */ + char *oldFilter; + /** * Current byte offset on filter text. */ @@ -136,8 +141,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api); int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item); /* filter.c */ -bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted); -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted); +bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted); +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted); /* list.c */ void _bmItemListFreeList(struct _bmItemList *list); diff --git a/lib/menu.c b/lib/menu.c index acf9ffc..c5c98ad 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -7,27 +7,11 @@ /** * Filter function map. */ -static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) = { +static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) = { _bmFilterDmenu, /* BM_FILTER_DMENU */ _bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */ }; -static void _bmMenuFilter(bmMenu *menu) -{ - assert(menu); - - if (!strlen(menu->filter) || !menu->items.list || menu->items.count <= 0) { - _bmItemListFreeList(&menu->filtered); - return; - } - - unsigned int count, selected; - bmItem **filtered = filterFunc[menu->filterMode](menu, &count, &selected); - - _bmItemListSetItemsNoCopy(&menu->filtered, filtered, count); - menu->index = selected; -} - int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item) { assert(menu); @@ -87,6 +71,9 @@ void bmMenuFree(bmMenu *menu) if (menu->title) free(menu->title); + if (menu->oldFilter) + free(menu->oldFilter); + bmMenuFreeItems(menu); free(menu); } @@ -138,12 +125,7 @@ void* bmMenuGetUserdata(bmMenu *menu) void bmMenuSetFilterMode(bmMenu *menu, bmFilterMode mode) { assert(menu); - - bmFilterMode oldMode = mode; menu->filterMode = (mode >= BM_FILTER_MODE_LAST ? BM_FILTER_MODE_DMENU : mode); - - if (oldMode != mode) - _bmMenuFilter(menu); } /** @@ -202,13 +184,7 @@ const char* bmMenuGetTitle(const bmMenu *menu) int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) { assert(menu); - - int ret = _bmItemListAddItemAt(&menu->items, item, index); - - if (ret) - _bmMenuFilter(menu); - - return ret; + return _bmItemListAddItemAt(&menu->items, item, index); } /** @@ -220,12 +196,7 @@ int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) */ int bmMenuAddItem(bmMenu *menu, bmItem *item) { - int ret = _bmItemListAddItem(&menu->items, item); - - if (ret) - _bmMenuFilter(menu); - - return ret; + return _bmItemListAddItem(&menu->items, item); } /** @@ -250,7 +221,6 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) if (ret) { _bmItemListRemoveItem(&menu->selection, item); _bmItemListRemoveItem(&menu->filtered, item); - _bmMenuFilter(menu); } return ret; @@ -274,7 +244,6 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) if (ret) { _bmItemListRemoveItem(&menu->selection, item); _bmItemListRemoveItem(&menu->filtered, item); - _bmMenuFilter(menu); } return ret; @@ -393,7 +362,6 @@ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) if (ret) { _bmItemListFreeList(&menu->selection); _bmItemListFreeList(&menu->filtered); - _bmMenuFilter(menu); } return ret; @@ -447,6 +415,57 @@ void bmMenuRender(const bmMenu *menu) menu->renderApi.render(menu); } +/** + * Trigger filtering of menu manually. + * This is useful when adding new items and want to dynamically see them filtered. + * + * Do note that filtering might be heavy, so you should only call it after batch manipulation of items. + * Not after manipulation of each single item. + * + * @param menu bmMenu instance which to filter. + */ +void bmMenuFilter(bmMenu *menu) +{ + assert(menu); + + char addition = 0; + size_t len = strlen(menu->filter); + + if (!len || !menu->items.list || menu->items.count <= 0) { + _bmItemListFreeList(&menu->filtered); + + if (menu->oldFilter) + free(menu->oldFilter); + + menu->oldFilter = NULL; + return; + } + + if (menu->oldFilter) { + size_t oldLen = strlen(menu->oldFilter); + addition = (oldLen < len && !memcmp(menu->oldFilter, menu->filter, oldLen)); + } + + if (menu->oldFilter && addition && menu->filtered.count <= 0) + return; + + if (menu->oldFilter && !strcmp(menu->filter, menu->oldFilter)) + return; + + unsigned int count, selected; + bmItem **filtered = filterFunc[menu->filterMode](menu, addition, &count, &selected); + + _bmItemListSetItemsNoCopy(&menu->filtered, filtered, count); + menu->index = selected; + + if (count) { + if (menu->oldFilter) + free(menu->oldFilter); + + menu->oldFilter = _bmStrdup(menu->filter); + } +} + /** * Poll key and unicode from underlying UI toolkit. * @@ -485,8 +504,6 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) unsigned int itemsCount; bmMenuGetFilteredItems(menu, &itemsCount); - char *oldFilter = _bmStrdup(menu->filter); - switch (key) { case BM_KEY_LEFT: { @@ -619,11 +636,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) default: break; } - if (oldFilter && strcmp(oldFilter, menu->filter)) - _bmMenuFilter(menu); - - if (oldFilter) - free(oldFilter); + bmMenuFilter(menu); switch (key) { case BM_KEY_RETURN: return BM_RUN_RESULT_SELECTED; -- cgit v1.2.3-70-g09d2 From 8e0dd1ab39d3d041b4093e2c43fdf6715da1add3 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 23:05:37 +0300 Subject: Make it possible to set filter text manually. --- lib/bemenu.h | 16 ++++++++++++++++ lib/menu.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) (limited to 'lib/menu.c') diff --git a/lib/bemenu.h b/lib/bemenu.h index 6348693..d348ffa 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -400,6 +400,22 @@ void bmItemSetUserdata(bmItem *item, void *userdata); */ void* bmItemGetUserdata(bmItem *item); +/** + * Set filter text to bmMenu instance. + * + * @param menu bmMenu instance where to set filter. + * @param filter Null terminated C "string" to act as filter. + */ +void bmMenuSetFilter(bmMenu *menu, const char *filter); + +/** + * Get filter text from bmMenu instance. + * + * @param menu bmMenu instance where to get filter. + * @return Const pointer to current filter text, may be **NULL** if empty. + */ +const char* bmMenuGetFilter(bmMenu *menu); + /** * Set text to bmItem instance. * diff --git a/lib/menu.c b/lib/menu.c index c5c98ad..cb0d3ee 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -116,6 +116,36 @@ void* bmMenuGetUserdata(bmMenu *menu) return menu->userdata; } +/** + * Set filter text to bmMenu instance. + * + * @param menu bmMenu instance where to set filter. + * @param filter Null terminated C "string" to act as filter. + */ +void bmMenuSetFilter(bmMenu *menu, const char *filter) +{ + assert(menu); + + if (!filter) { + memset(menu->filter, 0, sizeof(menu->filter)); + return; + } + + strncpy(menu->filter, filter, sizeof(menu->filter)); +} + +/** + * Get filter text from bmMenu instance. + * + * @param menu bmMenu instance where to get filter. + * @return Const pointer to current filter text, may be **NULL** if empty. + */ +const char* bmMenuGetFilter(bmMenu *menu) +{ + assert(menu); + return menu->filter; +} + /** * Set active filter mode to bmMenu instance. * -- cgit v1.2.3-70-g09d2 From dfdb810099017ae3468ee8db9d3a3da2e21be250 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 00:40:21 +0300 Subject: This is actually better without the count check. --- lib/menu.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'lib/menu.c') diff --git a/lib/menu.c b/lib/menu.c index cb0d3ee..edbcf73 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -488,12 +488,10 @@ void bmMenuFilter(bmMenu *menu) _bmItemListSetItemsNoCopy(&menu->filtered, filtered, count); menu->index = selected; - if (count) { - if (menu->oldFilter) - free(menu->oldFilter); + if (menu->oldFilter) + free(menu->oldFilter); - menu->oldFilter = _bmStrdup(menu->filter); - } + menu->oldFilter = _bmStrdup(menu->filter); } /** -- cgit v1.2.3-70-g09d2 From 9e9b671fa3863aecabdee4a9befdef91ef29f6f3 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 14:36:12 +0300 Subject: Remove highlight logic from filters, it does not belong there. Plus it seems dmenu always just highlights first item on filter change. --- lib/filter.c | 21 ++++++--------------- lib/internal.h | 4 ++-- lib/menu.c | 8 ++++---- 3 files changed, 12 insertions(+), 21 deletions(-) (limited to 'lib/menu.c') diff --git a/lib/filter.c b/lib/filter.c index 4674b5d..836ad83 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -79,17 +79,15 @@ fail: * @param addition This will be 1, if filter is same as previous filter with something appended. * @param fstrstr Substring function used to match items. * @param outNmemb unsigned int reference to filtered items outNmemb. - * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const char *a, const char *b), int (*fstrncmp)(const char *a, const char *b, size_t len), unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const char *a, const char *b), int (*fstrncmp)(const char *a, const char *b, size_t len), unsigned int *outNmemb) { assert(menu); assert(fstrstr); assert(fstrncmp); assert(outNmemb); - assert(outHighlighted); - *outNmemb = *outHighlighted = 0; + *outNmemb = 0; unsigned int itemsCount; bmItem **items; @@ -110,8 +108,6 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c if (!(buffer = _bmFilterTokenize(menu, &tokv, &tokc))) goto fail; - bmItem *highlighted = bmMenuGetHighlightedItem(menu); - size_t len = (tokc ? strlen(tokv[0]) : 0); unsigned int i, f, e; for (e = f = i = 0; i < itemsCount; ++i) { @@ -126,9 +122,6 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c continue; } - if (item == highlighted) - *outHighlighted = f; - if (tokc && item->text && !fstrncmp(tokv[0], item->text, len + 1)) { /* exact matches */ memmove(&filtered[1], filtered, f * sizeof(bmItem*)); filtered[0] = item; @@ -164,12 +157,11 @@ fail: * @param menu bmMenu instance to filter. * @param addition This will be 1, if filter is same as previous filter with something appended. * @param outNmemb unsigned int reference to filtered items outNmemb. - * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb) { - return _bmFilterDmenuFun(menu, addition, strstr, strncmp, outNmemb, outHighlighted); + return _bmFilterDmenuFun(menu, addition, strstr, strncmp, outNmemb); } /** @@ -178,12 +170,11 @@ bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, uns * @param menu bmMenu instance to filter. * @param addition This will be 1, if filter is same as previous filter with something appended. * @param outNmemb unsigned int reference to filtered items outNmemb. - * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb) { - return _bmFilterDmenuFun(menu, addition, _bmStrupstr, _bmStrnupcmp, outNmemb, outHighlighted); + return _bmFilterDmenuFun(menu, addition, _bmStrupstr, _bmStrnupcmp, outNmemb); } /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/internal.h b/lib/internal.h index c33c441..2acd6f5 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -141,8 +141,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api); int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item); /* filter.c */ -bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted); -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted); +bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb); +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb); /* list.c */ void _bmItemListFreeList(struct _bmItemList *list); diff --git a/lib/menu.c b/lib/menu.c index edbcf73..0457da8 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -7,7 +7,7 @@ /** * Filter function map. */ -static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) = { +static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, char addition, unsigned int *outNmemb) = { _bmFilterDmenu, /* BM_FILTER_DMENU */ _bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */ }; @@ -482,11 +482,11 @@ void bmMenuFilter(bmMenu *menu) if (menu->oldFilter && !strcmp(menu->filter, menu->oldFilter)) return; - unsigned int count, selected; - bmItem **filtered = filterFunc[menu->filterMode](menu, addition, &count, &selected); + unsigned int count; + bmItem **filtered = filterFunc[menu->filterMode](menu, addition, &count); _bmItemListSetItemsNoCopy(&menu->filtered, filtered, count); - menu->index = selected; + menu->index = 0; if (menu->oldFilter) free(menu->oldFilter); -- cgit v1.2.3-70-g09d2 From ad4e0425a6860803e5f31a08c349cd99e02e2847 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 18:42:30 +0300 Subject: Make page scrolling work like it should. (Shfit+pgup/pgdwn for old behaviour) --- lib/bemenu.h | 2 ++ lib/draw/curses.c | 13 +++++++++++++ lib/internal.h | 5 +++++ lib/menu.c | 17 ++++++++++++++++- 4 files changed, 36 insertions(+), 1 deletion(-) (limited to 'lib/menu.c') diff --git a/lib/bemenu.h b/lib/bemenu.h index d348ffa..0b0769d 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -77,6 +77,8 @@ typedef enum bmKey { BM_KEY_END, BM_KEY_PAGE_UP, BM_KEY_PAGE_DOWN, + BM_KEY_SHIFT_PAGE_UP, + BM_KEY_SHIFT_PAGE_DOWN, BM_KEY_BACKSPACE, BM_KEY_DELETE, BM_KEY_LINE_DELETE_LEFT, diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 653a07d..b54693f 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -198,6 +198,12 @@ static void _bmDrawCursesRender(const bmMenu *menu) curses.refresh(); } +static unsigned int _bmDrawCursesDisplayedCount(const bmMenu *menu) +{ + (void)menu; + return (curses.stdscr ? curses.getmaxy(curses.stdscr) : 0); +} + static void _bmDrawCursesEndWin(void) { freopen(TTY, "w", stdout); @@ -258,6 +264,12 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) case KEY_NPAGE: /* Page down */ return BM_KEY_PAGE_DOWN; + case 398: /* S-Page up */ + return BM_KEY_SHIFT_PAGE_UP; + + case 396: /* S-Page down */ + return BM_KEY_SHIFT_PAGE_DOWN; + case 8: /* C-h */ case 127: /* Delete */ case KEY_BACKSPACE: @@ -361,6 +373,7 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) #undef bmLoadFunction + api->displayedCount = _bmDrawCursesDisplayedCount; api->getKey = _bmDrawCursesGetKey; api->render = _bmDrawCursesRender; api->free = _bmDrawCursesFree; diff --git a/lib/internal.h b/lib/internal.h index 2acd6f5..b3ca87c 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -27,6 +27,11 @@ struct _bmItem { * Renderers should be able to fill this one as they see fit. */ struct _bmRenderApi { + /** + * Get count of displayed items by the underlying renderer. + */ + unsigned int (*displayedCount)(const bmMenu *menu); + /** * If the underlying renderer is a UI toolkit. (curses, etc...) * There might be possibility to get user input, and this should be thus implemented. diff --git a/lib/menu.c b/lib/menu.c index 0457da8..e819dd5 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -532,6 +532,13 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) unsigned int itemsCount; bmMenuGetFilteredItems(menu, &itemsCount); + unsigned int displayed = 0; + if (menu->renderApi.displayedCount) + displayed = menu->renderApi.displayedCount(menu); + + if (!displayed) + displayed = itemsCount; + switch (key) { case BM_KEY_LEFT: { @@ -569,10 +576,18 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_PAGE_UP: - menu->index = 0; + menu->index = (menu->index < displayed ? 0 : menu->index - (displayed - 1)); break; case BM_KEY_PAGE_DOWN: + menu->index = (menu->index + displayed >= itemsCount ? itemsCount - 1 : menu->index + (displayed - 1)); + break; + + case BM_KEY_SHIFT_PAGE_UP: + menu->index = 0; + break; + + case BM_KEY_SHIFT_PAGE_DOWN: menu->index = itemsCount - 1; break; -- cgit v1.2.3-70-g09d2 From 3ad042c625d931b7043a857bb49cb0660258f343 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 18:44:03 +0300 Subject: S-Return (C-t, insert in curses) should return input, C-Return is mark. (C-r, C-space in curses) --- lib/bemenu.h | 1 + lib/draw/curses.c | 6 ++++++ lib/menu.c | 4 +++- 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'lib/menu.c') diff --git a/lib/bemenu.h b/lib/bemenu.h index 0b0769d..9ef684f 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -88,6 +88,7 @@ typedef enum bmKey { BM_KEY_ESCAPE, BM_KEY_RETURN, BM_KEY_SHIFT_RETURN, + BM_KEY_CONTROL_RETURN, BM_KEY_UNICODE, BM_KEY_LAST } bmKey; diff --git a/lib/draw/curses.c b/lib/draw/curses.c index b54693f..4b467f6 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -291,7 +291,13 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) case 9: /* Tab */ return BM_KEY_TAB; + case 0: /* C-Space */ case 18: /* C-r */ + return BM_KEY_CONTROL_RETURN; + + case 20: /* C-t */ + case 331: /* Insert */ + _bmDrawCursesEndWin(); return BM_KEY_SHIFT_RETURN; case 10: /* Return */ diff --git a/lib/menu.c b/lib/menu.c index e819dd5..a53451d 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -663,7 +663,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) } break; - case BM_KEY_SHIFT_RETURN: + case BM_KEY_CONTROL_RETURN: case BM_KEY_RETURN: { bmItem *highlighted = bmMenuGetHighlightedItem(menu); @@ -672,6 +672,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) } break; + case BM_KEY_SHIFT_RETURN: case BM_KEY_ESCAPE: _bmItemListFreeList(&menu->selection); break; @@ -682,6 +683,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) bmMenuFilter(menu); switch (key) { + case BM_KEY_SHIFT_RETURN: case BM_KEY_RETURN: return BM_RUN_RESULT_SELECTED; case BM_KEY_ESCAPE: return BM_RUN_RESULT_CANCEL; default: break; -- cgit v1.2.3-70-g09d2 From f03e03cdd9c267b9f3c171ba57799d876d80d819 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 20:52:29 +0300 Subject: Make filter to pointer. --- lib/draw/curses.c | 2 +- lib/internal.h | 12 ++++++++---- lib/menu.c | 40 ++++++++++++++++++---------------------- lib/util.c | 41 +++++++++++++++++++++++++++++------------ 4 files changed, 56 insertions(+), 39 deletions(-) (limited to 'lib/menu.c') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 946275e..3d7871e 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -182,7 +182,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) curses.erase(); int titleLen = (menu->title ? strlen(menu->title) + 1 : 0); - _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", menu->filter); + _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", (menu->filter ? menu->filter : "")); if (menu->title) { curses.attron(COLOR_PAIR(1)); diff --git a/lib/internal.h b/lib/internal.h index 6d116a0..4f6afbe 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -103,15 +103,19 @@ struct _bmMenu { /** * Text used to filter matches. - * XXX: Change this to a pointer? */ - char filter[1024]; + char *filter; /** * Used as optimization. */ char *oldFilter; + /** + * Size of filter buffer + */ + size_t filterSize; + /** * Current byte offset on filter text. */ @@ -173,7 +177,7 @@ size_t _bmUtf8RuneNext(const char *string, size_t start); size_t _bmUtf8RunePrev(const char *string, size_t start); size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len); size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *outRuneWidth); -size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *outRuneWidth); -size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *outRuneWidth); +size_t _bmUtf8RuneInsert(char **string, size_t *bufSize, size_t start, const char *rune, unsigned int u8len, size_t *outRuneWidth); +size_t _bmUnicodeInsert(char **string, size_t *bufSize, size_t start, unsigned int unicode, size_t *outRuneWidth); /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/menu.c b/lib/menu.c index a53451d..f414772 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -71,6 +71,9 @@ void bmMenuFree(bmMenu *menu) if (menu->title) free(menu->title); + if (menu->filter) + free(menu->filter); + if (menu->oldFilter) free(menu->oldFilter); @@ -126,12 +129,11 @@ void bmMenuSetFilter(bmMenu *menu, const char *filter) { assert(menu); - if (!filter) { - memset(menu->filter, 0, sizeof(menu->filter)); - return; - } + if (menu->filter) + free(menu->filter); - strncpy(menu->filter, filter, sizeof(menu->filter)); + menu->filter = (filter ? _bmStrdup(filter) : NULL); + menu->filterSize = (filter ? strlen(filter) : 0); } /** @@ -426,7 +428,7 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); - if (strlen(menu->filter)) + if (menu->filter && strlen(menu->filter)) return _bmItemListGetItems(&menu->filtered, outNmemb); return _bmItemListGetItems(&menu->items, outNmemb); @@ -459,7 +461,7 @@ void bmMenuFilter(bmMenu *menu) assert(menu); char addition = 0; - size_t len = strlen(menu->filter); + size_t len = (menu->filter ? strlen(menu->filter) : 0); if (!len || !menu->items.list || menu->items.count <= 0) { _bmItemListFreeList(&menu->filtered); @@ -561,7 +563,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_END: - menu->cursor = strlen(menu->filter); + menu->cursor = (menu->filter ? strlen(menu->filter) : 0); menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); break; @@ -619,17 +621,17 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) case BM_KEY_WORD_DELETE: { - while (menu->cursor < strlen(menu->filter) && !isspace(menu->filter[menu->cursor])) { + while (menu->cursor < (menu->filter ? strlen(menu->filter) : 0) && !isspace(menu->filter[menu->cursor])) { unsigned int oldCursor = menu->cursor; menu->cursor += _bmUtf8RuneNext(menu->filter, menu->cursor); menu->cursesCursor += _bmUtf8RuneWidth(menu->filter + oldCursor, menu->cursor - oldCursor); } - while (menu->cursor > 0 && isspace(menu->filter[menu->cursor - 1])) { + while (menu->cursor > 0 && menu->filter && isspace(menu->filter[menu->cursor - 1])) { unsigned int oldCursor = menu->cursor; menu->cursor -= _bmUtf8RunePrev(menu->filter, menu->cursor); menu->cursesCursor -= _bmUtf8RuneWidth(menu->filter + menu->cursor, oldCursor - menu->cursor); } - while (menu->cursor > 0 && !isspace(menu->filter[menu->cursor - 1])) { + while (menu->cursor > 0 && menu->filter && !isspace(menu->filter[menu->cursor - 1])) { size_t width; menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); menu->cursesCursor -= width; @@ -640,24 +642,18 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) case BM_KEY_UNICODE: { size_t width; - menu->cursor += _bmUnicodeInsert(menu->filter, sizeof(menu->filter) - 1, menu->cursor, unicode, &width); + menu->cursor += _bmUnicodeInsert(&menu->filter, &menu->filterSize, menu->cursor, unicode, &width); menu->cursesCursor += width; } break; case BM_KEY_TAB: { + const char *text; bmItem *highlighted = bmMenuGetHighlightedItem(menu); - if (highlighted && bmItemGetText(highlighted)) { - const char *text = bmItemGetText(highlighted); - size_t len = strlen(text); - - if (len > sizeof(menu->filter) - 1) - len = sizeof(menu->filter) - 1; - - memset(menu->filter, 0, strlen(menu->filter)); - memcpy(menu->filter, text, len); - menu->cursor = strlen(menu->filter); + if (highlighted && (text = bmItemGetText(highlighted))) { + bmMenuSetFilter(menu, text); + menu->cursor = (menu->filter ? strlen(menu->filter) : 0); menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); } } diff --git a/lib/util.c b/lib/util.c index bcb27cb..ca00514 100644 --- a/lib/util.c +++ b/lib/util.c @@ -228,29 +228,45 @@ size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *outRuneWidth) /** * Insert UTF8 rune to buffer. * - * @param string Null terminated C "string". - * @param bufSize Size of the buffer. + * @param inOutString Reference to buffer. + * @param inOutBufSize Reference to size of the buffer. * @param start Start offset where to insert to. (cursor) * @param rune Buffer to insert to string. * @param u8len Byte length of the rune. * @param outRuneWidth Reference to size_t, return number of columns for inserted rune, or -1 on failure. * @return Number of bytes inserted to buffer. */ -size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *outRuneWidth) +size_t _bmUtf8RuneInsert(char **inOutString, size_t *inOutBufSize, size_t start, const char *rune, unsigned int u8len, size_t *outRuneWidth) { - assert(string); + assert(inOutString); + assert(inOutBufSize); if (outRuneWidth) *outRuneWidth = 0; - size_t len = strlen(string); - if (len + u8len >= bufSize) + size_t len = (*inOutString ? strlen(*inOutString) : 0); + if (!*inOutString && !(*inOutString = calloc(1, (*inOutBufSize = u8len + 1)))) return 0; + if (len + u8len >= *inOutBufSize) { + void *tmp; + if (!(tmp = realloc(*inOutString, (*inOutBufSize * 2)))) { + if (!(tmp = malloc((*inOutBufSize * 2)))) + return 0; + + memcpy(tmp, *inOutString, *inOutBufSize); + free(*inOutString); + } + + memset(tmp + *inOutBufSize, 0, *inOutBufSize); + *inOutString = tmp; + *inOutBufSize *= 2; + } + if (u8len == 1 && !isprint(*rune)) return 0; - char *str = string + start; + char *str = *inOutString + start; memmove(str + u8len, str, len - start); memcpy(str, rune, u8len); @@ -262,16 +278,17 @@ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char /** * Insert unicode character to UTF8 buffer. * - * @param string Null terminated C "string". - * @param bufSize Size of the buffer. + * @param inOutString Reference to buffer. + * @param inOutBufSize Reference to size of the buffer. * @param start Start offset where to insert to. (cursor) * @param unicode Unicode character to insert. * @param outRuneWidth Reference to size_t, return number of columns for inserted rune, or -1 on failure. * @return Number of bytes inserted to buffer. */ -size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *outRuneWidth) +size_t _bmUnicodeInsert(char **inOutString, size_t *inOutBufSize, size_t start, unsigned int unicode, size_t *outRuneWidth) { - assert(string); + assert(inOutString); + assert(inOutBufSize); char u8len = ((unicode < 0x80) ? 1 : ((unicode < 0x800) ? 2 : ((unicode < 0x10000) ? 3 : 4))); char mb[5] = { 0, 0, 0, 0 }; @@ -285,7 +302,7 @@ size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int mb[0] |= (unicode >> (i * 6 - 6)); } - return _bmUtf8RuneInsert(string, bufSize, start, mb, u8len, outRuneWidth); + return _bmUtf8RuneInsert(inOutString, inOutBufSize, start, mb, u8len, outRuneWidth); } /* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From 01ff5f48edde73364d8f4b11f4f5480c8cc86789 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 20:56:00 +0300 Subject: The list will be copied now. --- lib/bemenu.h | 2 -- lib/menu.c | 10 +++++++--- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'lib/menu.c') diff --git a/lib/bemenu.h b/lib/bemenu.h index 9ef684f..37c7d1e 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -252,8 +252,6 @@ bmItem* bmMenuGetHighlightedItem(const bmMenu *menu); /** * Set selected items to bmMenu instance. * - * @warning The list won't be copied, do not free it. - * * @param menu bmMenu instance where items will be set. * @param items Array of bmItem pointers to set. * @param nmemb Total count of items in array. diff --git a/lib/menu.c b/lib/menu.c index f414772..e31c25f 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -346,8 +346,6 @@ bmItem* bmMenuGetHighlightedItem(const bmMenu *menu) /** * Set selected items to bmMenu instance. * - * @warning The list won't be copied, do not free it. - * * @param menu bmMenu instance where items will be set. * @param items Array of bmItem pointers to set. * @param nmemb Total count of items in array. @@ -356,7 +354,13 @@ bmItem* bmMenuGetHighlightedItem(const bmMenu *menu) int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int nmemb) { assert(menu); - return _bmItemListSetItemsNoCopy(&menu->selection, items, nmemb); + + bmItem **newItems; + if (!(newItems = calloc(sizeof(bmItem*), nmemb))) + return 0; + + memcpy(newItems, items, sizeof(bmItem*) * nmemb); + return _bmItemListSetItemsNoCopy(&menu->selection, newItems, nmemb); } /** -- cgit v1.2.3-70-g09d2 From 994d6074bc390492764adb51dc2235d245145afa Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 23:09:35 +0300 Subject: Stop assuming filter is static array anymore. --- lib/menu.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'lib/menu.c') diff --git a/lib/menu.c b/lib/menu.c index e31c25f..5d2b37b 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -547,7 +547,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) switch (key) { case BM_KEY_LEFT: - { + if (menu->filter) { unsigned int oldCursor = menu->cursor; menu->cursor -= _bmUtf8RunePrev(menu->filter, menu->cursor); menu->cursesCursor -= _bmUtf8RuneWidth(menu->filter + menu->cursor, oldCursor - menu->cursor); @@ -555,7 +555,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_RIGHT: - { + if (menu->filter) { unsigned int oldCursor = menu->cursor; menu->cursor += _bmUtf8RuneNext(menu->filter, menu->cursor); menu->cursesCursor += _bmUtf8RuneWidth(menu->filter + oldCursor, menu->cursor - oldCursor); @@ -568,7 +568,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) case BM_KEY_END: menu->cursor = (menu->filter ? strlen(menu->filter) : 0); - menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); + menu->cursesCursor = (menu->filter ? _bmUtf8StringScreenWidth(menu->filter) : 0); break; case BM_KEY_UP: @@ -598,7 +598,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_BACKSPACE: - { + if (menu->filter) { size_t width; menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); menu->cursesCursor -= width; @@ -606,11 +606,12 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_DELETE: - _bmUtf8RuneRemove(menu->filter, menu->cursor + 1, NULL); + if (menu->filter) + _bmUtf8RuneRemove(menu->filter, menu->cursor + 1, NULL); break; case BM_KEY_LINE_DELETE_LEFT: - { + if (menu->filter) { while (menu->cursor > 0) { size_t width; menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); @@ -620,22 +621,23 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_LINE_DELETE_RIGHT: - menu->filter[menu->cursor] = 0; + if (menu->filter) + menu->filter[menu->cursor] = 0; break; case BM_KEY_WORD_DELETE: - { - while (menu->cursor < (menu->filter ? strlen(menu->filter) : 0) && !isspace(menu->filter[menu->cursor])) { + if (menu->filter) { + while (menu->cursor < strlen(menu->filter) && !isspace(menu->filter[menu->cursor])) { unsigned int oldCursor = menu->cursor; menu->cursor += _bmUtf8RuneNext(menu->filter, menu->cursor); menu->cursesCursor += _bmUtf8RuneWidth(menu->filter + oldCursor, menu->cursor - oldCursor); } - while (menu->cursor > 0 && menu->filter && isspace(menu->filter[menu->cursor - 1])) { + while (menu->cursor > 0 && isspace(menu->filter[menu->cursor - 1])) { unsigned int oldCursor = menu->cursor; menu->cursor -= _bmUtf8RunePrev(menu->filter, menu->cursor); menu->cursesCursor -= _bmUtf8RuneWidth(menu->filter + menu->cursor, oldCursor - menu->cursor); } - while (menu->cursor > 0 && menu->filter && !isspace(menu->filter[menu->cursor - 1])) { + while (menu->cursor > 0 && !isspace(menu->filter[menu->cursor - 1])) { size_t width; menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); menu->cursesCursor -= width; @@ -658,7 +660,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) if (highlighted && (text = bmItemGetText(highlighted))) { bmMenuSetFilter(menu, text); menu->cursor = (menu->filter ? strlen(menu->filter) : 0); - menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); + menu->cursesCursor = (menu->filter ? _bmUtf8StringScreenWidth(menu->filter) : 0); } } break; -- cgit v1.2.3-70-g09d2 From 617c4ab827d9c71a122034594bb1080607f50398 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:25:16 +0300 Subject: Add selection wrapping (-w). --- client/client.c | 3 ++- lib/bemenu.h | 16 ++++++++++++++++ lib/internal.h | 5 +++++ lib/menu.c | 34 ++++++++++++++++++++++++++++++++-- 4 files changed, 55 insertions(+), 3 deletions(-) (limited to 'lib/menu.c') diff --git a/client/client.c b/client/client.c index 0e6bf15..7df00bb 100644 --- a/client/client.c +++ b/client/client.c @@ -91,7 +91,7 @@ static void parseArgs(int *argc, char **argv[]) * or parse them before running getopt.. */ for (;;) { - int opt = getopt_long(*argc, *argv, "hviw:l:I:p:Ibf:m", opts, NULL); + int opt = getopt_long(*argc, *argv, "hviwl:I:p:I:bfm:", opts, NULL); if (opt < 0) break; @@ -190,6 +190,7 @@ int main(int argc, char **argv) bmMenuSetTitle(menu, client.title); bmMenuSetFilterMode(menu, client.filterMode); + bmMenuSetWrap(menu, client.wrap); readItemsToMenuFromStdin(menu); diff --git a/lib/bemenu.h b/lib/bemenu.h index 172c6ed..5457cca 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -202,6 +202,22 @@ void bmMenuSetFilterMode(bmMenu *menu, bmFilterMode mode); */ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu); +/** + * Set selection wrapping on/off. + * + * @param menu bmMenu instance where to toggle selection wrapping. + * @param int 1 == on, 0 == off. + */ +void bmMenuSetWrap(bmMenu *menu, int wrap); + +/** + * Get selection wrapping state. + * + * @param menu bmMenu instance where to get selection wrapping state. + * @return int for wrap state. + */ +int bmMenuGetWrap(const bmMenu *menu); + /** * Set title to bmMenu instance. * diff --git a/lib/internal.h b/lib/internal.h index 4f6afbe..2fc9393 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -141,6 +141,11 @@ struct _bmMenu { * Drawing mode used in menu instance. */ bmDrawMode drawMode; + + /** + * Should selection be wrapped? + */ + char wrap; }; /* draw/curses.c */ diff --git a/lib/menu.c b/lib/menu.c index 5d2b37b..d949430 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -172,6 +172,30 @@ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu) return menu->filterMode; } +/** + * Set selection wrapping on/off. + * + * @param menu bmMenu instance where to toggle selection wrapping. + * @param int 1 == on, 0 == off. + */ +void bmMenuSetWrap(bmMenu *menu, int wrap) +{ + assert(menu); + menu->wrap = (wrap ? 1 : 0); +} + +/** + * Get selection wrapping state. + * + * @param menu bmMenu instance where to get selection wrapping state. + * @return int for wrap state. + */ +int bmMenuGetWrap(const bmMenu *menu) +{ + assert(menu); + return menu->wrap; +} + /** * Set title to bmMenu instance. * @@ -572,13 +596,19 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_UP: - if (menu->index > 0) + if (menu->index > 0) { menu->index--; + } else if (menu->wrap) { + menu->index = itemsCount - 1; + } break; case BM_KEY_DOWN: - if (menu->index < itemsCount - 1) + if (menu->index < itemsCount - 1) { menu->index++; + } else if (menu->wrap) { + menu->index = 0; + } break; case BM_KEY_PAGE_UP: -- cgit v1.2.3-70-g09d2 From 5556779dd2b6ac48ef7e137ca0da3ac9190238bf Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 21:18:50 +0300 Subject: Consistency. --- lib/menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/menu.c') diff --git a/lib/menu.c b/lib/menu.c index d949430..b7ab021 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -45,7 +45,7 @@ bmMenu* bmMenuNew(bmDrawMode drawMode) status = _bmDrawCursesInit(&menu->renderApi); break; - default:break; + default: break; } if (status == 0) { -- cgit v1.2.3-70-g09d2