From 908749cfdfa037b6f5fd0f5f7b2fa66080329156 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 28 Mar 2014 21:33:20 +0200 Subject: Add basic API code. => bmMenu instancing. => "Rendering" => bmMenu releasing. --- lib/bemenu.h | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 lib/bemenu.h (limited to 'lib/bemenu.h') diff --git a/lib/bemenu.h b/lib/bemenu.h new file mode 100644 index 0000000..f4823b4 --- /dev/null +++ b/lib/bemenu.h @@ -0,0 +1,38 @@ +/** + * @file bemenu.h + * + * Public header + */ + +/** + * Draw mode constants for setting bmMenu instance draw mode. + */ +typedef enum bmDrawMode { + BM_DRAW_MODE_NONE +} bmDrawMode; + +typedef struct _bmMenu bmMenu; + +/** + * 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); + +/** + * Release bmMenu instance. + * + * @param menu bmMenu instance to be freed from memory. + */ +void bmMenuFree(bmMenu *menu); + +/** + * Render bmMenu instance using chosen draw method. + * + * @param menu bmMenu instance to be rendered. + */ +void bmMenuRender(bmMenu *menu); + +/* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From 06c8bde93fe4a4ae366cd69f8fb2745ce95cefb4 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 28 Mar 2014 21:59:34 +0200 Subject: Provide BM_DRAW_MODE_LAST for enumerating draw modes. --- lib/bemenu.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib/bemenu.h') diff --git a/lib/bemenu.h b/lib/bemenu.h index f4823b4..3fdfb0b 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -6,9 +6,13 @@ /** * Draw mode constants for setting bmMenu instance draw mode. + * + * BM_DRAW_MODE_LAST is provided for enumerating draw modes. + * Instancing with it however provides exactly same functionality as BM_DRAW_MODE_NONE. */ typedef enum bmDrawMode { - BM_DRAW_MODE_NONE + BM_DRAW_MODE_NONE, + BM_DRAW_MODE_LAST } bmDrawMode; typedef struct _bmMenu bmMenu; -- cgit v1.2.3-70-g09d2 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 --- client/client.c | 95 +++++++++- lib/CMakeLists.txt | 7 +- lib/bemenu.c | 60 ------ lib/bemenu.h | 227 ++++++++++++++++++++++- lib/draw/curses.c | 258 +++++++++++++++++++++++++- lib/filter.c | 64 +++++++ lib/internal.h | 39 +++- lib/item.c | 75 ++++++++ lib/menu.c | 527 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/util.c | 226 +++++++++++++++++++++++ 10 files changed, 1504 insertions(+), 74 deletions(-) delete mode 100644 lib/bemenu.c create mode 100644 lib/filter.c create mode 100644 lib/item.c create mode 100644 lib/menu.c create mode 100644 lib/util.c (limited to 'lib/bemenu.h') diff --git a/client/client.c b/client/client.c index 559651f..1c30498 100644 --- a/client/client.c +++ b/client/client.c @@ -6,8 +6,83 @@ */ #include +#include +#include +#include +#include #include +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); + + if (stream == NULL || feof(stream) || ferror(stream)) + return -1; + + allocated = *outAllocated; + buffer = *outLine; + + if (buffer == NULL || allocated == 0) { + if (!(buffer = calloc(1, (allocated = 1024) + 1))) + return -1; + } + + for (s = buffer;;) { + if (fgets(s, allocated - (s - buffer), stream) == NULL) + return -1; + + len = strlen(s); + if (feof(stream)) + break; + + if (len > 0 && s[len - 1] == '\n') + break; + + if (len + 1 >= allocated - (s - buffer)) { + void *tmp = realloc(buffer, 2 * allocated); + if (!tmp) + break; + + buffer = tmp; + s = buffer + allocated - 1; + memset(s, 0, allocated - (s - buffer)); + allocated *= 2; + } else { + s += len; + } + } + + *outAllocated = allocated; + *outLine = buffer; + + if (s[len - 1] == '\n') + s[len - 1] = 0; + + return s - buffer + len; +} + +static void readItemsToMenuFromStdin(bmMenu *menu) +{ + ptrdiff_t len; + size_t size; + char *line = NULL; + + while ((len = getLine(&line, &size, stdin)) != -1) { + bmItem *item = bmItemNew((len > 0 ? line : NULL)); + if (!item) + break; + + bmMenuAddItem(menu, item); + } + + if (line) + free(line); +} + /** * Main method * @@ -21,16 +96,30 @@ int main(int argc, char **argv) { (void)argc, (void)argv; - bmMenu *menu = bmMenuNew(BM_DRAW_MODE_NONE); + bmMenu *menu = bmMenuNew(BM_DRAW_MODE_CURSES); if (!menu) return EXIT_FAILURE; - bmMenuRender(menu); + bmMenuSetTitle(menu, "bemenu"); + readItemsToMenuFromStdin(menu); + + bmKey key; + unsigned int unicode; + int status = 0; + do { + bmMenuRender(menu); + key = bmMenuGetKey(menu, &unicode); + } while ((status = bmMenuRunWithKey(menu, key, unicode)) == BM_RUN_RESULT_RUNNING); + + if (status == BM_RUN_RESULT_SELECTED) { + bmItem *item = bmMenuGetSelectedItem(menu); + printf("%s\n", bmItemGetText(item)); + } bmMenuFree(menu); - return EXIT_SUCCESS; + return (status == BM_RUN_RESULT_SELECTED ? EXIT_SUCCESS : EXIT_FAILURE); } /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index cf489fd..e669ef5 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,6 +1,9 @@ # Sources SET(BEMENU_SOURCE - bemenu.c + menu.c + item.c + filter.c + util.c draw/curses.c ) SET(BEMENU_INCLUDE) @@ -22,6 +25,6 @@ ENDIF () # Compile INCLUDE_DIRECTORIES(${BEMENU_INCLUDE}) ADD_LIBRARY(bemenu ${BEMENU_SOURCE}) -TARGET_LINK_LIBRARIES(bemenu ${BEMENU_LIBRARIES}) +TARGET_LINK_LIBRARIES(bemenu ${BEMENU_LIBRARIES} dl) # vim: set ts=8 sw=4 tw=0 : diff --git a/lib/bemenu.c b/lib/bemenu.c deleted file mode 100644 index d4ae77e..0000000 --- a/lib/bemenu.c +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @file bemenu.c - */ - -#include "internal.h" -#include -#include - -/** - * 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; - - switch (menu->drawMode) { - default:break; - } - - 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(); - - free(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. - */ -void bmMenuRender(bmMenu *menu) -{ - assert(menu != NULL); - - if (menu->renderApi.render) - menu->renderApi.render(menu->items, menu->itemsCount); -} - -/* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/bemenu.h b/lib/bemenu.h index 3fdfb0b..b4b9b30 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -5,17 +5,71 @@ */ /** - * Draw mode constants for setting bmMenu instance draw mode. + * Draw mode constants for bmMenu instance draw mode. * * BM_DRAW_MODE_LAST is provided for enumerating draw modes. * Instancing with it however provides exactly same functionality as BM_DRAW_MODE_NONE. */ typedef enum bmDrawMode { BM_DRAW_MODE_NONE, + BM_DRAW_MODE_CURSES, BM_DRAW_MODE_LAST } bmDrawMode; +/** + * Filter mode constants for bmMenu instance filter mode. + * + * BM_FILTER_MODE_LAST is provided for enumerating filter modes. + * Using it as filter mode however provides exactly same functionality as BM_FILTER_MODE_DMENU. + */ +typedef enum bmFilterMode { + BM_FILTER_MODE_DMENU, + BM_FILTER_MODE_DMENU_CASE_INSENSITIVE, + BM_FILTER_MODE_LAST +} 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. + */ +typedef enum bmRunResult { + BM_RUN_RESULT_RUNNING, + BM_RUN_RESULT_SELECTED, + BM_RUN_RESULT_CANCEL, +} bmRunResult; + +/** + * Key constants. + * + * BM_KEY_LAST is provided for enumerating keys. + */ +typedef enum bmKey { + BM_KEY_NONE, + BM_KEY_UP, + BM_KEY_DOWN, + BM_KEY_LEFT, + BM_KEY_RIGHT, + BM_KEY_HOME, + BM_KEY_END, + BM_KEY_PAGE_UP, + BM_KEY_PAGE_DOWN, + BM_KEY_BACKSPACE, + BM_KEY_DELETE, + BM_KEY_LINE_DELETE_LEFT, + BM_KEY_LINE_DELETE_RIGHT, + BM_KEY_WORD_DELETE, + BM_KEY_TAB, + BM_KEY_ESCAPE, + BM_KEY_RETURN, + BM_KEY_UNICODE, + BM_KEY_LAST +} bmKey; + typedef struct _bmMenu bmMenu; +typedef struct _bmItem bmItem; /** * Create new bmMenu instance. @@ -32,11 +86,180 @@ bmMenu* bmMenuNew(bmDrawMode drawMode); */ void bmMenuFree(bmMenu *menu); +/** + * Release items inside bmMenu instance. + * + * @param menu bmMenu instance which items will be freed from memory. + */ +void bmMenuFreeItems(bmMenu *menu); + +/** + * 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); + +/** + * Get active filter mode from bmMenu instance. + * + * @param menu bmMenu instance where to get filter mode. + * @return bmFilterMode constant. + */ +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. + */ +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. + */ +const char* bmMenuGetTitle(const bmMenu *menu); + +/** + * 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); + +/** + * 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); + +/** + * 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); + +/** + * Remove item from bmMenu instance. + * 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. + */ +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. + */ +bmItem* bmMenuGetSelectedItem(const bmMenu *menu); + +/** + * 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); + +/** + * 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); + +/** + * 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); + /** * Render bmMenu instance using chosen draw method. * * @param menu bmMenu instance to be rendered. */ -void bmMenuRender(bmMenu *menu); +void bmMenuRender(const bmMenu *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); + +/** + * 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); + +/** + * 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. + */ +bmItem* bmItemNew(const char *text); + +/** + * Release bmItem instance. + * + * @param item bmItem instance to be freed from memory. + */ +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. + */ +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. + */ +const char* bmItemGetText(const bmItem *item); /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/draw/curses.c b/lib/draw/curses.c index c873496..48a146a 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -2,8 +2,260 @@ * @file curses.c */ -/* - * code goes here - */ +#include "../internal.h" +#include +#include +#include +#include +#include +#include +#include + +/* ncurses.h likes to define stuff for us. + * This unforunately mangles with our struct. */ +#undef erase +#undef refresh +#undef mvprintw +#undef move +#undef init_pair +#undef attroff +#undef attron +#undef getmaxx +#undef getmaxy +#undef timeout + +static struct curses { + void *handle; + WINDOW *stdscr; + WINDOW* (*initscr)(void); + int (*endwin)(void); + int (*refresh)(void); + int (*erase)(void); + int (*get_wch)(wint_t *wch); + int (*mvprintw)(int x, int y, const char *fmt, ...); + int (*move)(int x, int y); + int (*init_pair)(short color, short f, short b); + int (*attroff)(int attrs); + int (*attron)(int attrs); + int (*start_color)(void); + int (*getmaxx)(WINDOW *win); + int (*getmaxy)(WINDOW *win); + int (*keypad)(WINDOW *win, bool bf); + int *ESCDELAY; +} curses; + +static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) +{ + static int ncols = 0; + static char *buffer = NULL; + int new_ncols = curses.getmaxx(curses.stdscr); + + if (new_ncols <= 0) + return; + + if (!buffer || new_ncols > ncols) { + if (buffer) + free(buffer); + + ncols = new_ncols; + + if (!(buffer = calloc(1, ncols + 1))) + return; + } + + va_list args; + va_start(args, format); + int tlen = vsnprintf(NULL, 0, format, args) + 1; + if (tlen > ncols) + tlen = ncols; + va_end(args); + + va_start(args, format); + vsnprintf(buffer, tlen, format, args); + va_end(args); + + memset(buffer + tlen - 1, ' ', ncols - tlen + 1); + + if (pair > 0) + curses.attron(COLOR_PAIR(pair)); + + curses.mvprintw(y, 0, buffer); + + if (pair > 0) + curses.attroff(COLOR_PAIR(pair)); +} + +static void _bmDrawCursesRender(const bmMenu *menu) +{ + if (!curses.stdscr) { + freopen("/dev/tty", "rw", stdin); + setlocale(LC_CTYPE, ""); + if ((curses.stdscr = curses.initscr()) == NULL) + return; + + *curses.ESCDELAY = 25; + curses.keypad(curses.stdscr, true); + + curses.start_color(); + curses.init_pair(1, COLOR_BLACK, COLOR_RED); + curses.init_pair(2, COLOR_RED, COLOR_BLACK); + } + + const unsigned int lines = curses.getmaxy(curses.stdscr); + curses.erase(); + + size_t titleLen = (menu->title ? strlen(menu->title) + 1 : 0); + + _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", menu->filter); + + if (menu->title) { + curses.attron(COLOR_PAIR(1)); + curses.mvprintw(0, 0, menu->title); + curses.attroff(COLOR_PAIR(1)); + } + + unsigned int i, cl = 1; + 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); + } + + curses.move(0, titleLen + menu->cursesCursor); + curses.refresh(); +} + +static void _bmDrawCursesEndWin(void) +{ + if (curses.endwin) + curses.endwin(); + + curses.stdscr = NULL; +} + +static bmKey _bmDrawCursesGetKey(unsigned int *unicode) +{ + assert(unicode != NULL); + *unicode = 0; + + if (!curses.stdscr) + return BM_KEY_NONE; + + curses.get_wch(unicode); + switch (*unicode) { + case 16: /* C-p */ + case KEY_UP: return BM_KEY_UP; + + case 14: /* C-n */ + case KEY_DOWN: return BM_KEY_DOWN; + + case 2: /* C-b */ + case KEY_LEFT: return BM_KEY_LEFT; + + case 6: /* C-f */ + case KEY_RIGHT: return BM_KEY_RIGHT; + + case 1: /* C-a */ + case KEY_HOME: return BM_KEY_HOME; + + case 5: /* C-e */ + case KEY_END: return BM_KEY_END; + + case KEY_PPAGE: return BM_KEY_PAGE_UP; + case KEY_NPAGE: return BM_KEY_PAGE_DOWN; + + case 8: /* C-h */ + case KEY_BACKSPACE: return BM_KEY_BACKSPACE; + + case 4: /* C-d */ + case KEY_DC: return BM_KEY_DELETE; + + case 21: return BM_KEY_LINE_DELETE_LEFT; /* C-u */ + case 11: return BM_KEY_LINE_DELETE_RIGHT; /* C-k */ + case 23: return BM_KEY_WORD_DELETE; /* C-w */ + + case 9: return BM_KEY_TAB; /* Tab */ + + case 10: /* Return */ + _bmDrawCursesEndWin(); + return BM_KEY_RETURN; + + case 7: /* C-g */ + case 27: /* Escape */ + _bmDrawCursesEndWin(); + return BM_KEY_ESCAPE; + + default: break; + } + + return BM_KEY_UNICODE; +} + +static void _bmDrawCursesFree(void) +{ + _bmDrawCursesEndWin(); + + if (curses.handle) + dlclose(curses.handle); + + memset(&curses, 0, sizeof(curses)); +} + +int _bmDrawCursesInit(struct _bmRenderApi *api) +{ + memset(&curses, 0, sizeof(curses)); + + /* FIXME: hardcoded and not cross-platform */ + curses.handle = dlopen("/usr/lib/libncursesw.so.5", RTLD_LAZY); + + if (!curses.handle) + return 0; + +#define bmLoadFunction(x) (curses.x = dlsym(curses.handle, #x)) + + if (!bmLoadFunction(initscr)) + goto function_pointer_exception; + if (!bmLoadFunction(endwin)) + goto function_pointer_exception; + if (!bmLoadFunction(refresh)) + goto function_pointer_exception; + if (!bmLoadFunction(get_wch)) + goto function_pointer_exception; + if (!bmLoadFunction(erase)) + goto function_pointer_exception; + if (!bmLoadFunction(mvprintw)) + goto function_pointer_exception; + if (!bmLoadFunction(move)) + goto function_pointer_exception; + if (!bmLoadFunction(init_pair)) + goto function_pointer_exception; + if (!bmLoadFunction(attroff)) + goto function_pointer_exception; + if (!bmLoadFunction(attron)) + goto function_pointer_exception; + if (!bmLoadFunction(start_color)) + goto function_pointer_exception; + if (!bmLoadFunction(getmaxx)) + goto function_pointer_exception; + if (!bmLoadFunction(getmaxy)) + goto function_pointer_exception; + if (!bmLoadFunction(keypad)) + goto function_pointer_exception; + if (!bmLoadFunction(ESCDELAY)) + goto function_pointer_exception; + +#undef bmLoadFunction + + api->getKey = _bmDrawCursesGetKey; + api->render = _bmDrawCursesRender; + api->free = _bmDrawCursesFree; + + return 1; + +function_pointer_exception: + _bmDrawCursesFree(); + return 0; +} /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/filter.c b/lib/filter.c new file mode 100644 index 0000000..b1cf668 --- /dev/null +++ b/lib/filter.c @@ -0,0 +1,64 @@ +/** + * @file filter.c + */ + +#include "internal.h" +#include +#include +#include + +/** + * 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. + * @return Pointer to array of bmItem pointers. + */ +bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selected) +{ + assert(menu != NULL); + assert(count != NULL); + assert(selected != NULL); + *count = *selected = 0; + + /* FIXME: not real dmenu like filtering at all */ + + bmItem **filtered = calloc(menu->itemsCount, sizeof(bmItem*)); + if (!filtered) + return NULL; + + unsigned int i, f; + for (f = i = 0; i < menu->itemsCount; ++i) { + bmItem *item = menu->items[i]; + if (item->text && strstr(item->text, menu->filter)) { + if (f == 0 || item == bmMenuGetSelectedItem(menu)) + *selected = f; + filtered[f++] = item; + } + } + + return _bmShrinkItemList(&filtered, menu->itemsCount, (*count = 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. + * @return Pointer to array of bmItem pointers. + */ +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *count, unsigned int *selected) +{ + assert(menu != NULL); + assert(count != NULL); + assert(selected != NULL); + *count = *selected = 0; + + /* FIXME: stub */ + + return NULL; +} + +/* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/internal.h b/lib/internal.h index c79ffd1..786e082 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -4,6 +4,10 @@ #include "bemenu.h" +#ifndef size_t +# include /* for size_t */ +#endif + /** * Internal bmItem struct that is not exposed to public. * Represents a single item in menu. @@ -17,7 +21,8 @@ struct _bmItem { * Renderers should be able to fill this one as they see fit. */ struct _bmRenderApi { - void (*render)(struct _bmItem **items, unsigned int nmemb); + bmKey (*getKey)(unsigned int *unicode); + void (*render)(const bmMenu *menu); void (*free)(void); }; @@ -25,10 +30,36 @@ struct _bmRenderApi { * Internal bmMenu struct that is not exposed to public. */ struct _bmMenu { - bmDrawMode drawMode; struct _bmRenderApi renderApi; - struct _bmItem **items; - unsigned int itemsCount; + struct _bmItem **items, **filteredItems; + char *title, filter[1024]; + unsigned int cursor, cursesCursor; + unsigned int itemsCount, allocatedCount; + unsigned int filteredCount; + unsigned int index; + bmFilterMode filterMode; + bmDrawMode drawMode; }; +/* draw/curses.c */ +int _bmDrawCursesInit(struct _bmRenderApi *api); + +/* menu.c */ +int _bmMenuShouldRenderItem(const bmMenu *menu, const bmItem *item); + +/* filter.c */ +bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selected); +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *count, unsigned int *selected); + +/* util.c */ +char* _bmStrdup(const char *s); +bmItem** _bmShrinkItemList(bmItem ***list, 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); + /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/item.c b/lib/item.c new file mode 100644 index 0000000..40ebb52 --- /dev/null +++ b/lib/item.c @@ -0,0 +1,75 @@ +/** + * @file item.c + */ + +#include "internal.h" +#include +#include +#include + +/** + * Allocate a new item. + * + * @param text Pointer to null terminated C "string", can be NULL for empty text. + * @return bmItem instance. + */ +bmItem* bmItemNew(const char *text) +{ + bmItem *item = calloc(1, sizeof(bmItem)); + + if (!item) + return NULL; + + bmItemSetText(item, text); + return item; +} + +/** + * Release bmItem instance. + * + * @param item bmItem instance to be freed from memory. + */ +void bmItemFree(bmItem *item) +{ + assert(item != NULL); + + if (item->text) + free(item->text); + + free(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. + */ +int bmItemSetText(bmItem *item, const char *text) +{ + assert(item != NULL); + + char *copy = NULL; + if (text && !(copy = _bmStrdup(text))) + return 0; + + if (item->text) + free(item->text); + + item->text = copy; + return 1; +} + +/** + * 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. + */ +const char* bmItemGetText(const bmItem *item) +{ + assert(item != NULL); + return item->text; +} + +/* vim: set ts=8 sw=4 tw=0 :*/ 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 :*/ diff --git a/lib/util.c b/lib/util.c new file mode 100644 index 0000000..8c9bc0f --- /dev/null +++ b/lib/util.c @@ -0,0 +1,226 @@ +/** + * @file util.c + */ + +#include "internal.h" +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include +#include + +/** + * Portable strdup. + * + * @param string C "string" to copy. + * @return Copy of the given C "string". + */ +char* _bmStrdup(const char *string) +{ + size_t len = strlen(string); + if (len == 0) + return NULL; + + void *copy = calloc(1, len + 1); + if (copy == NULL) + return NULL; + + return (char *)memcpy(copy, string, len); +} + +/** + * 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. + */ +bmItem** _bmShrinkItemList(bmItem ***list, size_t osize, size_t nsize) +{ + if (nsize >= osize) + return *list; + + void *tmp = malloc(sizeof(bmItem*) * nsize); + if (!tmp) + return *list; + + memcpy(tmp, *list, sizeof(bmItem*) * nsize); + free(*list); + *list = tmp; + return *list; +} + +/** + * Determite columns needed to display UTF8 string. + * + * @param string C "string" to determite. + * @return Number of columns, or -1 on failure. + */ +int _bmUtf8StringScreenWidth(const char *string) +{ + if (!string) + return 0; + + int num_char = mbstowcs(NULL, string, 0) + 1; + wchar_t *wstring = malloc((num_char + 1) * sizeof (wstring[0])); + + if (mbstowcs(wstring, string, num_char) == (size_t)(-1)) { + free(wstring); + return strlen(string); + } + + int length = wcswidth(wstring, num_char); + free(wstring); + return length; +} + +/** + * Figure out how many bytes to shift to next UTF8 rune. + * + * @param string C "string" which contains the runes. + * @param start Offset where to figure out next rune. (cursor) + * @return Number of bytes to next UTF8 rune. + */ +size_t _bmUtf8RuneNext(const char *string, size_t start) +{ + assert(string != NULL); + + size_t len = strlen(string), i = start; + if (len == 0 || len <= i || !*string) + return 0; + + while (++i < len && (string[i] & 0xc0) == 0x80); + return i - start; +} + +/** + * Figure out how many bytes to shift to previous UTF8 rune. + * + * @param string C "string" which contains the runes. + * @param start Offset where to figure out previous rune. (cursor) + * @return Number of bytes to previous UTF8 rune. + */ +size_t _bmUtf8RunePrev(const char *string, size_t start) +{ + assert(string != NULL); + + size_t len = strlen(string), i = start; + if (i == 0 || len < start || !*string) + return 0; + + while (--i > 0 && (string[i] & 0xc0) == 0x80); + return start - i; +} + +/** + * Figure out how many columns are needed to display UTF8 rune. + * + * @param rune Buffer which contains the rune. + * @param u8len Byte length of the rune. + * @return Number of columns, or -1 on failure. + */ +size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len) +{ + assert(rune != NULL); + char mb[5] = { 0, 0, 0, 0, 0 }; + memcpy(mb, rune, (u8len > 4 ? 4 : u8len)); + return _bmUtf8StringScreenWidth(mb); +} + +/** + * Remove previous UTF8 rune from buffer. + * + * @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. + * @return Number of bytes removed from buffer. + */ +size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *runeWidth) +{ + assert(string != NULL); + + if (runeWidth) + *runeWidth = 0; + + size_t len = strlen(string), oldStart = start; + if (len == 0 || len < start || !*string) + return 0; + + start -= _bmUtf8RunePrev(string, start); + + if (runeWidth) + *runeWidth = _bmUtf8RuneWidth(string + start, oldStart - start); + + memmove(string + start, string + oldStart, len - oldStart); + string[len - (oldStart - start)] = 0; + return (oldStart - start); +} + +/** + * Insert UTF8 rune to buffer. + * + * @param string Null terminated C "string". + * @param bufSize 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 runeWidth 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) +{ + assert(string != NULL); + + if (runeWidth) + *runeWidth = 0; + + size_t len = strlen(string); + if (len + u8len >= bufSize) + return 0; + + if (u8len == 1 && iscntrl(*rune)) + return 0; + + char *str = string + start; + memmove(str + u8len, str, len - start); + memcpy(str, rune, u8len); + + if (runeWidth) + *runeWidth = _bmUtf8RuneWidth(rune, u8len); + return u8len; +} + +/** + * Insert unicode character to UTF8 buffer. + * + * @param string Null terminated C "string". + * @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. + * @return Number of bytes inserted to buffer. + */ +size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *runeWidth) +{ + assert(string != NULL); + + char u8len = ((unicode < 0x80) ? 1 : ((unicode < 0x800) ? 2 : ((unicode < 0x10000) ? 3 : 4))); + char mb[5] = { 0, 0, 0, 0 }; + + if (u8len == 1) { + mb[0] = unicode; + } else { + size_t i, j; + for (i = j = u8len; j > 1; --j) mb[j - 1] = 0x80 | (0x3F & (unicode >> ((i - j) * 6))); + mb[0] = (~0) << (8 - i); + mb[0] |= (unicode >> (i * 6 - 6)); + } + + return _bmUtf8RuneInsert(string, bufSize, start, mb, u8len, runeWidth); +} + +/* vim: set ts=8 sw=4 tw=0 :*/ -- 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/bemenu.h') 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/bemenu.h') 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/bemenu.h') 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/bemenu.h') 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/bemenu.h') 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/bemenu.h') 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/bemenu.h') 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 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/bemenu.h') 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/bemenu.h') 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 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/bemenu.h') 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/bemenu.h') 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 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/bemenu.h') 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 06972a357983a4686dc37a2aa5a8b0bfd2dd4e7e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 22:12:44 +0300 Subject: CLI interface, aka option parsing. --- client/client.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++- lib/CMakeLists.txt | 7 ++- lib/bemenu.h | 21 ++++++++ lib/library.c | 8 +++ lib/version.h.in | 3 ++ 5 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 lib/library.c create mode 100644 lib/version.h.in (limited to 'lib/bemenu.h') diff --git a/client/client.c b/client/client.c index 2f95d52..fb94ea5 100644 --- a/client/client.c +++ b/client/client.c @@ -3,6 +3,140 @@ #include #include #include +#include + +static struct { + bmFilterMode filterMode; + int wrap; + unsigned int lines; + const char *title; + int selected; + int bottom; + int grab; + int monitor; +} client = { + BM_FILTER_MODE_DMENU, /* filterMode */ + 0, /* wrap */ + 0, /* lines */ + "bemenu", /* title */ + 0, /* selected */ + 0, /* bottom */ + 0, /* grab */ + 0 /* monitor */ +}; + +static void printVersion(const char *name) +{ + char *base = strrchr(name, '/'); + printf("%s v%s\n", (base ? base + 1 : name), bmVersion()); + puts(" Date: Sun, 13 Apr 2014 00:12:31 +0300 Subject: Fix bad syntax, and add defgroup for library. --- lib/bemenu.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'lib/bemenu.h') diff --git a/lib/bemenu.h b/lib/bemenu.h index ebafffa..f4fc954 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -7,6 +7,13 @@ typedef struct _bmMenu bmMenu; typedef struct _bmItem bmItem; +/** + * @defgroup Library + * @brief Library functions. + * + * Query library version, etc... + */ + /** * @defgroup Menu * @brief Menu container. @@ -22,7 +29,7 @@ typedef struct _bmItem bmItem; */ /** - * @addtogroub Library + * @addtogroup Library * @{ */ /** -- cgit v1.2.3-70-g09d2 From 4f72d188ada1e6ad725dfa9927112ec90c3ddf80 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:25:04 +0300 Subject: Fix documentation. --- lib/bemenu.h | 32 ++++++++++++++++---------------- lib/library.c | 7 +++++++ 2 files changed, 23 insertions(+), 16 deletions(-) (limited to 'lib/bemenu.h') diff --git a/lib/bemenu.h b/lib/bemenu.h index f4fc954..172c6ed 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -170,6 +170,22 @@ void bmMenuSetUserdata(bmMenu *menu, void *userdata); */ void* bmMenuGetUserdata(bmMenu *menu); +/** + * 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 active filter mode to bmMenu instance. * @@ -429,22 +445,6 @@ 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/library.c b/lib/library.c index 69e898b..f432eda 100644 --- a/lib/library.c +++ b/lib/library.c @@ -1,5 +1,12 @@ #include "version.h" +/** + * Get version of the library in 'major.minor.patch' format. + * + * @see @link http://semver.org/ Semantic Versioning @endlink + * + * @return Null terminated C "string" to version string. + */ const char *bmVersion(void) { return BM_VERSION; -- 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/bemenu.h') 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