From 1c511e30f540bcc7a79aa9ab96f9552097c1c6ec Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 28 Mar 2014 21:34:38 +0200 Subject: Remove custom output directory paths. --- client/CMakeLists.txt | 2 -- lib/CMakeLists.txt | 3 --- 2 files changed, 5 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 70ecd4b..91c4ea5 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,5 +1,3 @@ -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY bin) - # Sources SET(CLIENT_SOURCE client.c) SET(CLIENT_INCLUDE) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 46c532b..cf489fd 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,6 +1,3 @@ -SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY lib) -SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY lib) - # Sources SET(BEMENU_SOURCE bemenu.c -- cgit v1.2.3-70-g09d2 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. --- client/CMakeLists.txt | 2 +- client/client.c | 12 +++++++++--- lib/bemenu.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- lib/bemenu.h | 38 ++++++++++++++++++++++++++++++++++++++ lib/internal.h | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 134 insertions(+), 6 deletions(-) create mode 100644 lib/bemenu.h create mode 100644 lib/internal.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 91c4ea5..aceaaf5 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,6 +1,6 @@ # Sources SET(CLIENT_SOURCE client.c) -SET(CLIENT_INCLUDE) +SET(CLIENT_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/../lib") SET(CLIENT_LIBRARIES bemenu) # Warnings diff --git a/client/client.c b/client/client.c index de5468e..559651f 100644 --- a/client/client.c +++ b/client/client.c @@ -6,6 +6,7 @@ */ #include +#include /** * Main method @@ -20,9 +21,14 @@ int main(int argc, char **argv) { (void)argc, (void)argv; - /* - * code goes here - */ + bmMenu *menu = bmMenuNew(BM_DRAW_MODE_NONE); + + if (!menu) + return EXIT_FAILURE; + + bmMenuRender(menu); + + bmMenuFree(menu); return EXIT_SUCCESS; } diff --git a/lib/bemenu.c b/lib/bemenu.c index 2f92f27..a2caa13 100644 --- a/lib/bemenu.c +++ b/lib/bemenu.c @@ -2,8 +2,55 @@ * @file bemenu.c */ -/* - * code goes here +#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); + 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 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 :*/ diff --git a/lib/internal.h b/lib/internal.h new file mode 100644 index 0000000..d11ebe2 --- /dev/null +++ b/lib/internal.h @@ -0,0 +1,37 @@ +/** + * @file internal.h + */ + +#include "bemenu.h" + +/** + * Internal bmItem struct that is not exposed to public. + * Represents a single item in menu. + */ +struct _bmItem { + char *text; +} _bmItem; + +/** + * Internal bmRenderApi struct. + * Renderers should be able to fill this one as they see fit. + */ +struct _bmRenderApi { + void (*render)(struct _bmItem **items, unsigned int nmemb); + void (*free)(void); +} _bmRenderApi; + +/** + * Internal bmMenu struct that is not exposed to public. + */ +struct _bmMenu { + bmDrawMode drawMode; + struct _bmRenderApi renderApi; + struct _bmItem **items; + unsigned int itemsCount; +} _bmMenu; + + +void bmRenderNullInit(struct _bmRenderApi *api); + +/* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From c609e4025e9de5c5784e0210a11e34a5a06d7626 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 28 Mar 2014 21:40:46 +0200 Subject: Release renderer in bmMenuFree function. --- lib/bemenu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/bemenu.c b/lib/bemenu.c index a2caa13..d4ae77e 100644 --- a/lib/bemenu.c +++ b/lib/bemenu.c @@ -36,6 +36,10 @@ bmMenu* bmMenuNew(bmDrawMode drawMode) void bmMenuFree(bmMenu *menu) { assert(menu != NULL); + + if (menu->renderApi.free) + menu->renderApi.free(); + free(menu); } -- 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(-) 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 17250cfd3f86784bbee2130b8ba3341ded2346b0 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 28 Mar 2014 22:00:23 +0200 Subject: Add tests. --- test/CMakeLists.txt | 16 ++++++++++++++-- test/bmMenuNew.c | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 test/bmMenuNew.c diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8da6d4a..a70c749 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,17 @@ -INCLUDE(CTest) +SET(TESTS + "bmMenuNew" + ) -# TODO: write test CMakeLists +INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../lib") +FOREACH (test ${TESTS}) + ADD_EXECUTABLE(${test}_test ${test}.c) + TARGET_LINK_LIBRARIES(${test}_test bemenu) + + IF (WIN32) + ADD_TEST(${test}_test ${test}_test.exe) + ELSE () + ADD_TEST(${test}_test ${test}_test) + ENDIF () +ENDFOREACH (test) # vim: set ts=8 sw=4 tw=0 : diff --git a/test/bmMenuNew.c b/test/bmMenuNew.c new file mode 100644 index 0000000..f133584 --- /dev/null +++ b/test/bmMenuNew.c @@ -0,0 +1,23 @@ +#include +#include +#include + +int main(int argc, char **argv) +{ + (void)argc, (void)argv; + + // TEST: Instance bmMenu with all possible draw modes. + { + bmDrawMode i; + for (i = 0; i < BM_DRAW_MODE_LAST; ++i) { + bmMenu *menu = bmMenuNew(BM_DRAW_MODE_NONE); + assert(menu != NULL); + bmMenuRender(menu); + bmMenuFree(menu); + } + } + + return EXIT_SUCCESS; +} + +/* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From db55c12128763672dda8b1a9d524e2e676a829d2 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 28 Mar 2014 22:00:28 +0200 Subject: INCLUDE(CTest) must be in root CMakeLists for 'make test' to work --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index db0851d..65e2b3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,6 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8) PROJECT(bemenu) +INCLUDE(CTest) SET(BEMENU_NAME "bemenu") SET(BEMENU_DESCRIPTION "Dynamic menu library and client program inspired by dmenu") SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${chck_SOURCE_DIR}/CMake/modules) -- cgit v1.2.3-70-g09d2 From c4a8bf3631035890a5d3517bfa15f31e9b0492bf Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 28 Mar 2014 22:01:15 +0200 Subject: Add useful message for developer. --- test/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a70c749..f9d13a5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,4 +14,6 @@ FOREACH (test ${TESTS}) ENDIF () ENDFOREACH (test) +MESSAGE("-!- Use 'make test' to run tests") + # vim: set ts=8 sw=4 tw=0 : -- cgit v1.2.3-70-g09d2 From 13c470fb398ded391cd4b954cb98097aea6dd07d Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 28 Mar 2014 22:03:47 +0200 Subject: Remove unused function prototype. --- lib/internal.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/internal.h b/lib/internal.h index d11ebe2..388de08 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -31,7 +31,4 @@ struct _bmMenu { unsigned int itemsCount; } _bmMenu; - -void bmRenderNullInit(struct _bmRenderApi *api); - /* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From 92aa6367b815d337a9ad7a2c7a9fb6dba224423f Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 28 Mar 2014 22:14:13 +0200 Subject: These structs are not typedefs. --- lib/internal.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal.h b/lib/internal.h index 388de08..c79ffd1 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -10,7 +10,7 @@ */ struct _bmItem { char *text; -} _bmItem; +}; /** * Internal bmRenderApi struct. @@ -19,7 +19,7 @@ struct _bmItem { struct _bmRenderApi { void (*render)(struct _bmItem **items, unsigned int nmemb); void (*free)(void); -} _bmRenderApi; +}; /** * Internal bmMenu struct that is not exposed to public. @@ -29,6 +29,6 @@ struct _bmMenu { struct _bmRenderApi renderApi; struct _bmItem **items; unsigned int itemsCount; -} _bmMenu; +}; /* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From 18619437003543218a953790961c032d3fa7c7cc Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 29 Mar 2014 02:14:25 +0200 Subject: Make test actually use the iterated draw mode. --- test/bmMenuNew.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bmMenuNew.c b/test/bmMenuNew.c index f133584..e1b6161 100644 --- a/test/bmMenuNew.c +++ b/test/bmMenuNew.c @@ -10,7 +10,7 @@ int main(int argc, char **argv) { bmDrawMode i; for (i = 0; i < BM_DRAW_MODE_LAST; ++i) { - bmMenu *menu = bmMenuNew(BM_DRAW_MODE_NONE); + bmMenu *menu = bmMenuNew(i); assert(menu != NULL); bmMenuRender(menu); bmMenuFree(menu); -- cgit v1.2.3-70-g09d2 From a3498b25f414a888aa01ffd1cf9b2e44f6c54764 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Wed, 2 Apr 2014 21:43:53 +0300 Subject: Quote cmake variables --- doxygen/Doxyfile.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doxygen/Doxyfile.in b/doxygen/Doxyfile.in index 292260d..399b400 100644 --- a/doxygen/Doxyfile.in +++ b/doxygen/Doxyfile.in @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = @BEMENU_NAME@ +PROJECT_NAME = "@BEMENU_NAME@" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -44,7 +44,7 @@ PROJECT_NUMBER = # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = @BEMENU_DESCRIPTION@ +PROJECT_BRIEF = "@BEMENU_DESCRIPTION@" # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels @@ -743,7 +743,7 @@ WARN_LOGFILE = # spaces. # Note: If this tag is empty the current directory is searched. -INPUT = @CMAKE_CURRENT_SOURCE_DIR@/../lib @CMAKE_CURRENT_SOURCE_DIR@/../client +INPUT = "@CMAKE_CURRENT_SOURCE_DIR@/../lib" "@CMAKE_CURRENT_SOURCE_DIR@/../client" # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses -- 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 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 3c700a80d1e9f37778a3805d3ef1ec89c2e9763e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:25:18 +0300 Subject: Check if there is selected item at all. --- client/client.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/client.c b/client/client.c index 1c30498..2b2bb68 100644 --- a/client/client.c +++ b/client/client.c @@ -114,7 +114,9 @@ int main(int argc, char **argv) if (status == BM_RUN_RESULT_SELECTED) { bmItem *item = bmMenuGetSelectedItem(menu); - printf("%s\n", bmItemGetText(item)); + + if (item) + printf("%s\n", bmItemGetText(item)); } bmMenuFree(menu); -- cgit v1.2.3-70-g09d2 From 944e36b21d1bbc80680e228daa75f27b3eab4e4b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:25:37 +0300 Subject: Free filtered items. --- lib/menu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/menu.c b/lib/menu.c index 50311cf..7a39746 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -98,6 +98,9 @@ void bmMenuFree(bmMenu *menu) if (menu->title) free(menu->title); + if (menu->filteredItems) + free(menu->filteredItems); + bmMenuFreeItems(menu); free(menu); } -- cgit v1.2.3-70-g09d2 From dd4bddcf9877c5cf2f01cc035417f779565a0267 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:25:46 +0300 Subject: Do bounds checking correctly. --- lib/menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/menu.c b/lib/menu.c index 7a39746..b6116e9 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -272,7 +272,7 @@ bmItem* bmMenuGetSelectedItem(const bmMenu *menu) unsigned int count; bmItem **items = bmMenuGetFilteredItems(menu, &count); - if (!items || count < menu->index) + if (!items || count <= menu->index) return NULL; return items[menu->index]; -- cgit v1.2.3-70-g09d2 From 2615c43dc052617312ea102ab2a50cb449d9827b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:26:00 +0300 Subject: Move side effects after cleanup. --- lib/menu.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/menu.c b/lib/menu.c index b6116e9..5ea046a 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -510,12 +510,6 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) } break; - case BM_KEY_RETURN: - return BM_RUN_RESULT_SELECTED; - - case BM_KEY_ESCAPE: - return BM_RUN_RESULT_CANCEL; - default: break; } @@ -524,6 +518,13 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) if (oldFilter) free(oldFilter); + + switch (key) { + case BM_KEY_RETURN: return BM_RUN_RESULT_SELECTED; + case BM_KEY_ESCAPE: return BM_RUN_RESULT_CANCEL; + default: break; + } + return BM_RUN_RESULT_RUNNING; } -- cgit v1.2.3-70-g09d2 From 9fda2cdb2f031c61d125ab2f8684a143ed868b51 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:29:01 +0300 Subject: Fix static analyze errors. --- client/client.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/client.c b/client/client.c index 2b2bb68..df56511 100644 --- a/client/client.c +++ b/client/client.c @@ -32,8 +32,11 @@ static ptrdiff_t getLine(char **outLine, size_t *outAllocated, FILE *stream) } for (s = buffer;;) { - if (fgets(s, allocated - (s - buffer), stream) == NULL) + if (fgets(s, allocated - (s - buffer), stream) == NULL) { + *outAllocated = allocated; + *outLine = buffer; return -1; + } len = strlen(s); if (feof(stream)) @@ -68,7 +71,7 @@ static ptrdiff_t getLine(char **outLine, size_t *outAllocated, FILE *stream) static void readItemsToMenuFromStdin(bmMenu *menu) { ptrdiff_t len; - size_t size; + size_t size = 0; char *line = NULL; while ((len = getLine(&line, &size, stdin)) != -1) { -- cgit v1.2.3-70-g09d2 From cff1f37f970adafce1ac3a982ea1d338e609cfed Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:31:22 +0300 Subject: Do curses refresh before endwin. --- lib/draw/curses.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 48a146a..5c6ff4a 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -128,6 +128,9 @@ static void _bmDrawCursesRender(const bmMenu *menu) static void _bmDrawCursesEndWin(void) { + if (curses.refresh) + curses.refresh(); + if (curses.endwin) curses.endwin(); -- cgit v1.2.3-70-g09d2 From 4d920ad9e4aa344c1600e7419f75f6adb647b1b0 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:41:32 +0300 Subject: Make asserts and ifs more consistent. --- client/client.c | 10 +++++----- lib/draw/curses.c | 2 +- lib/filter.c | 12 ++++++------ lib/item.c | 6 +++--- lib/menu.c | 42 +++++++++++++++++++++--------------------- lib/util.c | 19 +++++++++++-------- test/bmMenuNew.c | 2 +- 7 files changed, 48 insertions(+), 45 deletions(-) diff --git a/client/client.c b/client/client.c index df56511..973e57a 100644 --- a/client/client.c +++ b/client/client.c @@ -17,22 +17,22 @@ static ptrdiff_t getLine(char **outLine, size_t *outAllocated, FILE *stream) size_t len = 0, allocated; char *s, *buffer; - assert(outLine != NULL); - assert(outAllocated != NULL); + assert(outLine); + assert(outAllocated); - if (stream == NULL || feof(stream) || ferror(stream)) + if (!stream || feof(stream) || ferror(stream)) return -1; allocated = *outAllocated; buffer = *outLine; - if (buffer == NULL || allocated == 0) { + if (!buffer || allocated == 0) { if (!(buffer = calloc(1, (allocated = 1024) + 1))) return -1; } for (s = buffer;;) { - if (fgets(s, allocated - (s - buffer), stream) == NULL) { + if (!fgets(s, allocated - (s - buffer), stream)) { *outAllocated = allocated; *outLine = buffer; return -1; diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 5c6ff4a..3582be5 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -139,7 +139,7 @@ static void _bmDrawCursesEndWin(void) static bmKey _bmDrawCursesGetKey(unsigned int *unicode) { - assert(unicode != NULL); + assert(unicode); *unicode = 0; if (!curses.stdscr) diff --git a/lib/filter.c b/lib/filter.c index b1cf668..d301415 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -17,9 +17,9 @@ */ bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selected) { - assert(menu != NULL); - assert(count != NULL); - assert(selected != NULL); + assert(menu); + assert(count); + assert(selected); *count = *selected = 0; /* FIXME: not real dmenu like filtering at all */ @@ -51,9 +51,9 @@ bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selecte */ bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *count, unsigned int *selected) { - assert(menu != NULL); - assert(count != NULL); - assert(selected != NULL); + assert(menu); + assert(count); + assert(selected); *count = *selected = 0; /* FIXME: stub */ diff --git a/lib/item.c b/lib/item.c index 40ebb52..beca6ed 100644 --- a/lib/item.c +++ b/lib/item.c @@ -31,7 +31,7 @@ bmItem* bmItemNew(const char *text) */ void bmItemFree(bmItem *item) { - assert(item != NULL); + assert(item); if (item->text) free(item->text); @@ -47,7 +47,7 @@ void bmItemFree(bmItem *item) */ int bmItemSetText(bmItem *item, const char *text) { - assert(item != NULL); + assert(item); char *copy = NULL; if (text && !(copy = _bmStrdup(text))) @@ -68,7 +68,7 @@ int bmItemSetText(bmItem *item, const char *text) */ const char* bmItemGetText(const bmItem *item) { - assert(item != NULL); + assert(item); return item->text; } diff --git a/lib/menu.c b/lib/menu.c index 5ea046a..ccdbf0d 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -15,7 +15,7 @@ static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *c static void _bmMenuFilter(bmMenu *menu) { - assert(menu != NULL); + assert(menu); if (menu->filteredItems) free(menu->filteredItems); @@ -90,7 +90,7 @@ bmMenu* bmMenuNew(bmDrawMode drawMode) */ void bmMenuFree(bmMenu *menu) { - assert(menu != NULL); + assert(menu); if (menu->renderApi.free) menu->renderApi.free(); @@ -112,7 +112,7 @@ void bmMenuFree(bmMenu *menu) */ void bmMenuFreeItems(bmMenu *menu) { - assert(menu != NULL); + assert(menu); unsigned int i; for (i = 0; i < menu->itemsCount; ++i) @@ -131,7 +131,7 @@ void bmMenuFreeItems(bmMenu *menu) */ void bmMenuSetFilterMode(bmMenu *menu, bmFilterMode mode) { - assert(menu != NULL); + assert(menu); bmFilterMode oldMode = mode; menu->filterMode = (mode >= BM_FILTER_MODE_LAST ? BM_FILTER_MODE_DMENU : mode); @@ -148,7 +148,7 @@ void bmMenuSetFilterMode(bmMenu *menu, bmFilterMode mode) */ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu) { - assert(menu != NULL); + assert(menu); return menu->filterMode; } @@ -160,7 +160,7 @@ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu) */ int bmMenuSetTitle(bmMenu *menu, const char *title) { - assert(menu != NULL); + assert(menu); char *copy = NULL; if (title && !(copy = _bmStrdup(title))) @@ -181,7 +181,7 @@ int bmMenuSetTitle(bmMenu *menu, const char *title) */ const char* bmMenuGetTitle(const bmMenu *menu) { - assert(menu != NULL); + assert(menu); return menu->title; } @@ -195,8 +195,8 @@ const char* bmMenuGetTitle(const bmMenu *menu) */ int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) { - assert(menu != NULL); - assert(item != NULL); + assert(menu); + assert(item); if (menu->itemsCount >= menu->allocatedCount && !_bmMenuGrowItems(menu)) return 0; @@ -232,7 +232,7 @@ int bmMenuAddItem(bmMenu *menu, bmItem *item) */ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) { - assert(menu != NULL); + assert(menu); unsigned int i = index; if (i >= menu->itemsCount) @@ -251,8 +251,8 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) */ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) { - assert(menu != NULL); - assert(item != NULL); + assert(menu); + assert(item); unsigned int i; for (i = 0; i < menu->itemsCount && menu->items[i] != item; ++i); @@ -267,7 +267,7 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) */ bmItem* bmMenuGetSelectedItem(const bmMenu *menu) { - assert(menu != NULL); + assert(menu); unsigned int count; bmItem **items = bmMenuGetFilteredItems(menu, &count); @@ -287,7 +287,7 @@ bmItem* bmMenuGetSelectedItem(const bmMenu *menu) */ bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *nmemb) { - assert(menu != NULL); + assert(menu); if (nmemb) *nmemb = menu->itemsCount; @@ -307,7 +307,7 @@ bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *nmemb) */ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *nmemb) { - assert(menu != NULL); + assert(menu); if (nmemb) *nmemb = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); @@ -326,9 +326,9 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *nmemb) */ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) { - assert(menu != NULL); + assert(menu); - if (items == NULL || nmemb == 0) { + if (!items || nmemb == 0) { bmMenuFreeItems(menu); return 1; } @@ -353,7 +353,7 @@ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) */ void bmMenuRender(const bmMenu *menu) { - assert(menu != NULL); + assert(menu); if (menu->renderApi.render) menu->renderApi.render(menu); @@ -370,8 +370,8 @@ void bmMenuRender(const bmMenu *menu) */ bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode) { - assert(menu != NULL); - assert(unicode != NULL); + assert(menu); + assert(unicode); *unicode = 0; bmKey key = BM_KEY_NONE; @@ -390,7 +390,7 @@ bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode) */ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) { - assert(menu != NULL); + assert(menu); char *oldFilter = _bmStrdup(menu->filter); unsigned int itemsCount = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); diff --git a/lib/util.c b/lib/util.c index 8c9bc0f..d948b05 100644 --- a/lib/util.c +++ b/lib/util.c @@ -18,6 +18,8 @@ */ char* _bmStrdup(const char *string) { + assert(string); + size_t len = strlen(string); if (len == 0) return NULL; @@ -41,6 +43,8 @@ char* _bmStrdup(const char *string) */ bmItem** _bmShrinkItemList(bmItem ***list, size_t osize, size_t nsize) { + assert(list); + if (nsize >= osize) return *list; @@ -62,8 +66,7 @@ bmItem** _bmShrinkItemList(bmItem ***list, size_t osize, size_t nsize) */ int _bmUtf8StringScreenWidth(const char *string) { - if (!string) - return 0; + assert(string); int num_char = mbstowcs(NULL, string, 0) + 1; wchar_t *wstring = malloc((num_char + 1) * sizeof (wstring[0])); @@ -87,7 +90,7 @@ int _bmUtf8StringScreenWidth(const char *string) */ size_t _bmUtf8RuneNext(const char *string, size_t start) { - assert(string != NULL); + assert(string); size_t len = strlen(string), i = start; if (len == 0 || len <= i || !*string) @@ -106,7 +109,7 @@ size_t _bmUtf8RuneNext(const char *string, size_t start) */ size_t _bmUtf8RunePrev(const char *string, size_t start) { - assert(string != NULL); + assert(string); size_t len = strlen(string), i = start; if (i == 0 || len < start || !*string) @@ -125,7 +128,7 @@ size_t _bmUtf8RunePrev(const char *string, size_t start) */ size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len) { - assert(rune != NULL); + assert(rune); char mb[5] = { 0, 0, 0, 0, 0 }; memcpy(mb, rune, (u8len > 4 ? 4 : u8len)); return _bmUtf8StringScreenWidth(mb); @@ -141,7 +144,7 @@ size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len) */ size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *runeWidth) { - assert(string != NULL); + assert(string); if (runeWidth) *runeWidth = 0; @@ -173,7 +176,7 @@ size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *runeWidth) */ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *runeWidth) { - assert(string != NULL); + assert(string); if (runeWidth) *runeWidth = 0; @@ -206,7 +209,7 @@ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char */ size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *runeWidth) { - assert(string != NULL); + assert(string); char u8len = ((unicode < 0x80) ? 1 : ((unicode < 0x800) ? 2 : ((unicode < 0x10000) ? 3 : 4))); char mb[5] = { 0, 0, 0, 0 }; diff --git a/test/bmMenuNew.c b/test/bmMenuNew.c index e1b6161..ccb572d 100644 --- a/test/bmMenuNew.c +++ b/test/bmMenuNew.c @@ -11,7 +11,7 @@ int main(int argc, char **argv) bmDrawMode i; for (i = 0; i < BM_DRAW_MODE_LAST; ++i) { bmMenu *menu = bmMenuNew(i); - assert(menu != NULL); + assert(menu); bmMenuRender(menu); bmMenuFree(menu); } -- cgit v1.2.3-70-g09d2 From 956494767c5f2fef7b5672ba60532224ea608d39 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 10:56:38 +0300 Subject: lolCase variables --- lib/draw/curses.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 3582be5..3afd9bc 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -48,16 +48,16 @@ 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); + int newNcols = curses.getmaxx(curses.stdscr); - if (new_ncols <= 0) + if (newNcols <= 0) return; - if (!buffer || new_ncols > ncols) { + if (!buffer || newNcols > ncols) { if (buffer) free(buffer); - ncols = new_ncols; + ncols = newNcols; if (!(buffer = calloc(1, ncols + 1))) return; -- cgit v1.2.3-70-g09d2 From efc5781aec94563871d56638d3ba92475b43e58b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 10:57:37 +0300 Subject: Consistency --- lib/draw/curses.c | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 3afd9bc..eecfcba 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -148,37 +148,54 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) curses.get_wch(unicode); switch (*unicode) { case 16: /* C-p */ - case KEY_UP: return BM_KEY_UP; + case KEY_UP: + return BM_KEY_UP; case 14: /* C-n */ - case KEY_DOWN: return BM_KEY_DOWN; + case KEY_DOWN: + return BM_KEY_DOWN; case 2: /* C-b */ - case KEY_LEFT: return BM_KEY_LEFT; + case KEY_LEFT: + return BM_KEY_LEFT; case 6: /* C-f */ - case KEY_RIGHT: return BM_KEY_RIGHT; + case KEY_RIGHT: + return BM_KEY_RIGHT; case 1: /* C-a */ - case KEY_HOME: return BM_KEY_HOME; + case KEY_HOME: + return BM_KEY_HOME; case 5: /* C-e */ - case KEY_END: return BM_KEY_END; + case KEY_END: + return BM_KEY_END; - case KEY_PPAGE: return BM_KEY_PAGE_UP; - case KEY_NPAGE: return BM_KEY_PAGE_DOWN; + case KEY_PPAGE: /* PAGE UP */ + return BM_KEY_PAGE_UP; + + case KEY_NPAGE: /* PAGE DOWN */ + return BM_KEY_PAGE_DOWN; case 8: /* C-h */ - case KEY_BACKSPACE: return BM_KEY_BACKSPACE; + case KEY_BACKSPACE: + return BM_KEY_BACKSPACE; case 4: /* C-d */ - case KEY_DC: return BM_KEY_DELETE; + case KEY_DC: + return BM_KEY_DELETE; + + case 21: /* C-u */ + return BM_KEY_LINE_DELETE_LEFT; + + case 11: /* C-k */ + return BM_KEY_LINE_DELETE_RIGHT; - 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 23: /* C-w */ + return BM_KEY_WORD_DELETE; - case 9: return BM_KEY_TAB; /* Tab */ + case 9: /* Tab */ + return BM_KEY_TAB; case 10: /* Return */ _bmDrawCursesEndWin(); -- cgit v1.2.3-70-g09d2 From f090160ab9b30ccad6b9b0e1b96bfdac29507177 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 11:00:26 +0300 Subject: Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3c711ab..07d3034 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Dynamic menu library and client program inspired by dmenu for C sources with following exceptions: * spaces not tabs * indentation size is 4 characters (spaces) + * function and variable names are camelCase except for global constants * [Standard style](http://legacy.python.org/dev/peps/pep-0008/) for Python * **Build system** - [CMake](http://www.cmake.org/) * **API documentation** - [Doxygen](http://www.stack.nl/~dimitri/doxygen/index.html) -- cgit v1.2.3-70-g09d2 From 722af2f7a0fe0e437e233cfaf7acadc3bcd0f24e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 11:07:37 +0300 Subject: Clarify which enums the functions are mapped to. --- lib/menu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/menu.c b/lib/menu.c index ccdbf0d..184c976 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -8,9 +8,12 @@ #include #include +/** + * Filter function map. + */ static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *count, unsigned int *selected) = { - _bmFilterDmenu, - _bmFilterDmenuCaseInsensitive + _bmFilterDmenu, /* BM_FILTER_DMENU */ + _bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */ }; static void _bmMenuFilter(bmMenu *menu) -- cgit v1.2.3-70-g09d2 From ff8ad3a804459ddc4e407b688b093b6f4543cf53 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 11:08:10 +0300 Subject: Remove @file from non-public library files. --- lib/draw/curses.c | 4 ---- lib/filter.c | 4 ---- lib/internal.h | 4 ---- lib/item.c | 4 ---- lib/menu.c | 4 ---- lib/util.c | 4 ---- 6 files changed, 24 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index eecfcba..a0f5351 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -1,7 +1,3 @@ -/** - * @file curses.c - */ - #include "../internal.h" #include #include diff --git a/lib/filter.c b/lib/filter.c index d301415..92e5683 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -1,7 +1,3 @@ -/** - * @file filter.c - */ - #include "internal.h" #include #include diff --git a/lib/internal.h b/lib/internal.h index 786e082..2220333 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -1,7 +1,3 @@ -/** - * @file internal.h - */ - #include "bemenu.h" #ifndef size_t diff --git a/lib/item.c b/lib/item.c index beca6ed..8320b9b 100644 --- a/lib/item.c +++ b/lib/item.c @@ -1,7 +1,3 @@ -/** - * @file item.c - */ - #include "internal.h" #include #include diff --git a/lib/menu.c b/lib/menu.c index 184c976..4ccb94a 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -1,7 +1,3 @@ -/** - * @file bemenu.c - */ - #include "internal.h" #include #include diff --git a/lib/util.c b/lib/util.c index d948b05..ae230c8 100644 --- a/lib/util.c +++ b/lib/util.c @@ -1,7 +1,3 @@ -/** - * @file util.c - */ - #include "internal.h" #define _XOPEN_SOURCE 700 #include -- cgit v1.2.3-70-g09d2 From 04dede1f96d965d3837ad9690a4ac076c99f10b3 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 12:07:23 +0300 Subject: Remove unused internal function. --- lib/internal.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/internal.h b/lib/internal.h index 2220333..a07d1cd 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -40,9 +40,6 @@ struct _bmMenu { /* 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); -- cgit v1.2.3-70-g09d2 From e230f03e54e0ad0c521177cd7a3f4c7628608d15 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 13:08:26 +0300 Subject: Fix bmMenuRender documentation --- lib/menu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/menu.c b/lib/menu.c index 4ccb94a..29a3058 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -345,10 +345,9 @@ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) } /** - * Create new bmMenu instance. + * Render bmMenu instance using chosen draw method. * - * @param drawMode Render method to be used for this menu instance. - * @return bmMenu for new menu instance, NULL if creation failed. + * @param menu bmMenu instance to be rendered. */ void bmMenuRender(const bmMenu *menu) { -- cgit v1.2.3-70-g09d2 From d53c91393d0c66892d25e4ef96cd23568d9fdcea Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 13:08:38 +0300 Subject: Fix bmMenuRunWithKey documentation --- lib/bemenu.h | 2 ++ lib/menu.c | 2 ++ 2 files changed, 4 insertions(+) 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 9a04c4a9a7ff424741f3b6f9b0e34a6f998da7b2 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 13:23:03 +0300 Subject: Document curses struct --- lib/draw/curses.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index a0f5351..c1bb013 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -20,6 +20,9 @@ #undef getmaxy #undef timeout +/** + * Dynamically loaded curses API. + */ static struct curses { void *handle; WINDOW *stdscr; -- cgit v1.2.3-70-g09d2 From 78713f23ac61f65a970b2870534327009213790b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 13:23:42 +0300 Subject: Document internal.h --- lib/internal.h | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/lib/internal.h b/lib/internal.h index a07d1cd..41fb926 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -9,6 +9,10 @@ * Represents a single item in menu. */ struct _bmItem { + /** + * Primary text shown on item as null terminated C "string". + * Matching will be done against this text as well. + */ char *text; }; @@ -17,8 +21,20 @@ struct _bmItem { * Renderers should be able to fill this one as they see fit. */ struct _bmRenderApi { + /** + * If the underlying renderer is a UI toolkit. (curses, etc...) + * There might be possibility to get user input, and this should be thus implemented. + */ bmKey (*getKey)(unsigned int *unicode); + + /** + * Tells underlying renderer to draw the menu. + */ void (*render)(const bmMenu *menu); + + /** + * Release underlying renderer. + */ void (*free)(void); }; @@ -26,14 +42,72 @@ struct _bmRenderApi { * Internal bmMenu struct that is not exposed to public. */ struct _bmMenu { + /** + * Underlying renderer access. + */ struct _bmRenderApi renderApi; - struct _bmItem **items, **filteredItems; - char *title, filter[1024]; - unsigned int cursor, cursesCursor; - unsigned int itemsCount, allocatedCount; + + /** + * All items contained in menu instance. + */ + struct _bmItem **items; + + /** + * Filtered/displayed items contained in menu instance. + */ + struct _bmItem **filteredItems; + + /** + * Menu instance title. + */ + char *title; + + /** + * Text used to filter matches. + * + * XXX: Change this to a pointer? + */ + char filter[1024]; + + /** + * Current byte offset on filter text. + */ + unsigned int cursor; + + /** + * Current column/cursor position on filter text. + */ + 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. + * This index is valid for the list returned by bmMenuGetFilteredItems. + */ unsigned int index; + + /** + * Current filtering method in menu instance. + */ bmFilterMode filterMode; + + /** + * Drawing mode used in menu instance. + */ bmDrawMode drawMode; }; -- cgit v1.2.3-70-g09d2 From 4a931c5ed4aaa0637bd9d41f7e45e9f22d9513ce Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 15:48:55 +0300 Subject: Nicer doxygen output --- CMakeLists.txt | 1 + doxygen/CMakeLists.txt | 2 + doxygen/Doxyfile.in | 16 +- doxygen/Mainpage.dox | 19 + doxygen/doxygen-qmi-style/README.md | 41 + doxygen/doxygen-qmi-style/footer.html | 20 + doxygen/doxygen-qmi-style/header.html | 55 ++ .../doxygen-qmi-style/navtree/ftv2mlastnode.png | Bin 0 -> 285 bytes doxygen/doxygen-qmi-style/navtree/ftv2mnode.png | Bin 0 -> 285 bytes .../doxygen-qmi-style/navtree/ftv2plastnode.png | Bin 0 -> 277 bytes doxygen/doxygen-qmi-style/navtree/ftv2pnode.png | Bin 0 -> 282 bytes doxygen/doxygen-qmi-style/navtree/navtree.css | 117 +++ doxygen/doxygen-qmi-style/qmi.css | 1033 ++++++++++++++++++++ doxygen/doxygen-qmi-style/search/search.css | 238 +++++ 14 files changed, 1535 insertions(+), 7 deletions(-) create mode 100644 doxygen/Mainpage.dox create mode 100644 doxygen/doxygen-qmi-style/README.md create mode 100644 doxygen/doxygen-qmi-style/footer.html create mode 100644 doxygen/doxygen-qmi-style/header.html create mode 100644 doxygen/doxygen-qmi-style/navtree/ftv2mlastnode.png create mode 100644 doxygen/doxygen-qmi-style/navtree/ftv2mnode.png create mode 100644 doxygen/doxygen-qmi-style/navtree/ftv2plastnode.png create mode 100644 doxygen/doxygen-qmi-style/navtree/ftv2pnode.png create mode 100644 doxygen/doxygen-qmi-style/navtree/navtree.css create mode 100644 doxygen/doxygen-qmi-style/qmi.css create mode 100644 doxygen/doxygen-qmi-style/search/search.css diff --git a/CMakeLists.txt b/CMakeLists.txt index 65e2b3b..ac22069 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ PROJECT(bemenu) INCLUDE(CTest) SET(BEMENU_NAME "bemenu") SET(BEMENU_DESCRIPTION "Dynamic menu library and client program inspired by dmenu") +SET(BEMENU_VERSION "1.0.0") SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${chck_SOURCE_DIR}/CMake/modules) # Compile library diff --git a/doxygen/CMakeLists.txt b/doxygen/CMakeLists.txt index e1a364b..0d2c406 100644 --- a/doxygen/CMakeLists.txt +++ b/doxygen/CMakeLists.txt @@ -4,6 +4,8 @@ IF (DOXYGEN_FOUND) CONFIGURE_FILE(Doxyfile.in Doxyfile @ONLY) ADD_CUSTOM_TARGET(doxygen ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/doxygen-qmi-style/navtree ${CMAKE_CURRENT_BINARY_DIR}/html + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/doxygen-qmi-style/search ${CMAKE_CURRENT_BINARY_DIR}/html/search WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating bemenu documentation with Doxygen" VERBATIM) MESSAGE("-!- Use 'make doxygen' to generate documentation") diff --git a/doxygen/Doxyfile.in b/doxygen/Doxyfile.in index 399b400..fcd3604 100644 --- a/doxygen/Doxyfile.in +++ b/doxygen/Doxyfile.in @@ -38,7 +38,7 @@ PROJECT_NAME = "@BEMENU_NAME@" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = "@BEMENU_VERSION@" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -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 = YES +BRIEF_MEMBER_DESC = NO # If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief # description of a member or function before the detailed description @@ -743,7 +743,9 @@ WARN_LOGFILE = # spaces. # Note: If this tag is empty the current directory is searched. -INPUT = "@CMAKE_CURRENT_SOURCE_DIR@/../lib" "@CMAKE_CURRENT_SOURCE_DIR@/../client" +INPUT = "@CMAKE_CURRENT_SOURCE_DIR@/Mainpage.dox" \ + "@CMAKE_CURRENT_SOURCE_DIR@/../lib" \ + "@CMAKE_CURRENT_SOURCE_DIR@/../client" # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -1035,7 +1037,7 @@ HTML_FILE_EXTENSION = .html # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_HEADER = +HTML_HEADER = "@CMAKE_CURRENT_SOURCE_DIR@/doxygen-qmi-style/header.html" # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard @@ -1045,7 +1047,7 @@ HTML_HEADER = # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_FOOTER = +HTML_FOOTER = "@CMAKE_CURRENT_SOURCE_DIR@/doxygen-qmi-style/footer.html" # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of @@ -1057,7 +1059,7 @@ HTML_FOOTER = # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_STYLESHEET = +HTML_STYLESHEET = "@CMAKE_CURRENT_SOURCE_DIR@/doxygen-qmi-style/qmi.css" # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- # defined cascading style sheet that is included after the standard style sheets @@ -1352,7 +1354,7 @@ DISABLE_INDEX = NO # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -GENERATE_TREEVIEW = NO +GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. diff --git a/doxygen/Mainpage.dox b/doxygen/Mainpage.dox new file mode 100644 index 0000000..8da4792 --- /dev/null +++ b/doxygen/Mainpage.dox @@ -0,0 +1,19 @@ +/** +@mainpage Main Page + +bemenu is a dynamic menu library and client program inspired by dmenu. +You can create flexible menu oriented programs using the library interface in your favorite language that has bemenu bindings avaialable. + +Unlike old dmenu approach, with library you don't have to feed unneccessary metadata into client program and parse the result. +Instead your program will be aware of the items and possible metadata inside the menu. +It's also possible to use multiple menus or dynamically remove/insert items. + +Features: + - Multiple layouts + - Different filtering algorithms: + - Vanilla dmenu filtering + - Rendering backends (UI toolkits are loaded dynamically, not depended): + - Curses + +bemenu also provides 'bemenu' executable that is compatible with dmenu interface. +*/ diff --git a/doxygen/doxygen-qmi-style/README.md b/doxygen/doxygen-qmi-style/README.md new file mode 100644 index 0000000..7fb3e13 --- /dev/null +++ b/doxygen/doxygen-qmi-style/README.md @@ -0,0 +1,41 @@ +Qmi is a "**Q**t **Mi**nimal" theme for the Doxygen HTML documentation. +It based on official Qt4 documentation's style. + +# How to setup + +To use `qmi` style make the following changes in your Doxyfile: + + # Project section + BRIEF_MEMBER_DESC = NO + + # HTML section + HTML_HEADER = ${path_to_qmi}/header.html + HTML_FOOTER = ${path_to_qmi}/footer.html + HTML_STYLESHEET = ${path_to_qmi}/qmi.css + +**NOTE**: + +* If you use **_tree navigation panel_** then copy contents of the `navtree` dir to the documentation html dir. +* If you use **_search_** feature then copy contents of the `search` dir to the `html/search`. + +# Examples + +If you want to see `qmi` style in action then use the following links with examples: + +* [Qwt docs](http://skozlovf.github.com/doxygen-qmi-style/qwt) +* [libxml++ docs](http://skozlovf.github.com/doxygen-qmi-style/libxmlpp) (with tree navigation and search) + + +## Screenshots + +* **Main page**: + + ![](http://skozlovf.github.com/doxygen-qmi-style/shot1.png) + +* **Index page**: + + ![](http://skozlovf.github.com/doxygen-qmi-style/shot2.png) + +* **Member description**: + + ![](http://skozlovf.github.com/doxygen-qmi-style/shot3.png) diff --git a/doxygen/doxygen-qmi-style/footer.html b/doxygen/doxygen-qmi-style/footer.html new file mode 100644 index 0000000..14115b4 --- /dev/null +++ b/doxygen/doxygen-qmi-style/footer.html @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/doxygen/doxygen-qmi-style/header.html b/doxygen/doxygen-qmi-style/header.html new file mode 100644 index 0000000..0b634ee --- /dev/null +++ b/doxygen/doxygen-qmi-style/header.html @@ -0,0 +1,55 @@ + + + + + + + +$projectname: $title +$title + + + +$treeview +$search +$mathjax + +$extrastylesheet + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + +
+
$projectname + $projectnumber +
+
$projectbrief
+
+
$projectbrief
+
$searchbox
+
+ diff --git a/doxygen/doxygen-qmi-style/navtree/ftv2mlastnode.png b/doxygen/doxygen-qmi-style/navtree/ftv2mlastnode.png new file mode 100644 index 0000000..6dfeb5d Binary files /dev/null and b/doxygen/doxygen-qmi-style/navtree/ftv2mlastnode.png differ diff --git a/doxygen/doxygen-qmi-style/navtree/ftv2mnode.png b/doxygen/doxygen-qmi-style/navtree/ftv2mnode.png new file mode 100644 index 0000000..48e70f0 Binary files /dev/null and b/doxygen/doxygen-qmi-style/navtree/ftv2mnode.png differ diff --git a/doxygen/doxygen-qmi-style/navtree/ftv2plastnode.png b/doxygen/doxygen-qmi-style/navtree/ftv2plastnode.png new file mode 100644 index 0000000..2b99c65 Binary files /dev/null and b/doxygen/doxygen-qmi-style/navtree/ftv2plastnode.png differ diff --git a/doxygen/doxygen-qmi-style/navtree/ftv2pnode.png b/doxygen/doxygen-qmi-style/navtree/ftv2pnode.png new file mode 100644 index 0000000..02f42f7 Binary files /dev/null and b/doxygen/doxygen-qmi-style/navtree/ftv2pnode.png differ diff --git a/doxygen/doxygen-qmi-style/navtree/navtree.css b/doxygen/doxygen-qmi-style/navtree/navtree.css new file mode 100644 index 0000000..64fa6b5 --- /dev/null +++ b/doxygen/doxygen-qmi-style/navtree/navtree.css @@ -0,0 +1,117 @@ +#nav-tree .children_ul { + margin:0; + padding:4px; +} + +#nav-tree ul { + list-style:none outside none; + margin:0px; + padding:0px; +} + +#nav-tree li { + white-space:nowrap; + margin:0px; + padding:0px; +} + +#nav-tree .plus { + margin:0px; +} + +#nav-tree .selected { + background-image: none; + background-color: #B0B0B0; + color: #fff; +} + +#nav-tree img { + margin:0px; + padding:0px; + border:0px; + vertical-align: middle; +} + +#nav-tree a { + text-decoration:none; + padding:0px; + margin:0px; + outline:none; +} + +#nav-tree .label { + margin:0px; + padding:0px; +} + +#nav-tree .label a { + padding:2px; +} + +#nav-tree .selected a { + text-decoration:none; + padding:2px; + margin:0px; + color:#fff; +} + +#nav-tree .children_ul { + margin:0px; + padding:0px; +} + +#nav-tree .item { + margin:0px; + padding:0px; +} + +#nav-tree { + padding: 0px 0px; + background-image:none; + background-color: #F6F6F6; + font-size:14px; + overflow:auto; +} + +#doc-content { + overflow:auto; + display:block; + padding:0px; + margin:0px; +} + +#side-nav { + padding:0 4px 0 0; + margin: 0px; + display:block; + position: absolute; + left: 0px; + width: 300px; +} + +.ui-resizable .ui-resizable-handle { + display:block; +} + +.ui-resizable-e { + background: none; + background-color: #EBEBEB; + cursor:e-resize; + height:100%; + right:0; + top:0; + width:4px; +} + +.ui-resizable-handle { + display:none; + font-size:0.1px; + position:absolute; + z-index:1; +} + +#nav-tree-contents { + margin: 6px 0px 0px 0px; +} + + diff --git a/doxygen/doxygen-qmi-style/qmi.css b/doxygen/doxygen-qmi-style/qmi.css new file mode 100644 index 0000000..4f1a70b --- /dev/null +++ b/doxygen/doxygen-qmi-style/qmi.css @@ -0,0 +1,1033 @@ +/******************************************************************************* + * Qmi style css for the doxygen. + * Based on tabs.css + doxygen.css and Qt4 documentation look&feel. + * Sergey Kozlov. + ******************************************************************************/ + + +/*********************************************************** + * Globals. + **********************************************************/ +body +{ + background-color: white; + color: black; + margin: 0; +} + +body, table, div, p, dl +{ + font: normal 13px/1.2 Verdana; + color: #363534; +} + +/* Treeview. */ + +#nav-sync +{ + display:none; +} + +/* Header. */ + +#titlearea +{ + padding: 0 0 0 8px; + margin: 0px; +} + +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectname +{ + font-size: 24px; + font-weight: bold; +} + +#projectbrief +{ + color: #575757; + font-size: 0.9em; + margin: 0 0 10px 0; + padding: 0px; +} + +#projectnumber +{ + font-size: 0.5em; + margin: 0px; + padding: 0px; +} + +/* Footer. */ + +/* Bottom left label. */ +.qmi +{ + float:left; + text-align: left; + font-style: normal; + margin-left:10px; + padding-bottom: 6px; + color: #C2C2C2; +} + +.qmi a { color: #5E5E5E; } + +address { font-style: normal; } +address.footer +{ + text-align: right; + padding-right: 12px; + padding-bottom: 6px; +} + +hr.footer +{ + height: 1px; +} + + +/* Not used. */ +/* +img.footer +{ + border: 0px; + vertical-align: middle; +} +*/ + + +h1 { font-size: 150%; } +h2 { font-size: 120%; } +h3 { font-size: 100%; } +dt { font-weight: bold; } +dl { padding: 0 0 0 10px; } +dl.el { margin-left: -1cm; } + +hr +{ + height: 0px; + border: none; + border-top: 1px solid #C4C4C4; +} + +div.center +{ + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; +} + +div.center img { border: 0px; } + +/* Link to reference (classes etc). */ +a.el { font-weight: bold; } + +/** Use normal font on the index page. */ +.qindex + table a.el { font-weight: normal; } +/* a.elRef {} */ + +/* Links in code section. */ +a.code { font-weight: bold; } +a.codeRef { color: #4665A2; } + + +/* Code section */ +.fragment +{ + font-family: monospace, fixed; + font-size: 11px; +} + +pre.fragment +{ + padding: 4px 6px; + margin: 4px 20px 4px 10px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; + + background-color: #F6F6F6; + border-color: #E6E6E6; + border-width: 1px; + border-style: solid; + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; +} + + +/*********************************************************** + * Top navigation tabs. + * General info: + * navrow1 - div id of the first tab row. + * tabs, tabs2, tabs3 - are divs of of each tab row. + * tablist - list of tabs in a row. + * tablist li - tab contents. + **********************************************************/ + +#navrow1 { background-color: #EBEBEB; } + +.tabs, .tabs2, .tabs3 +{ + width: 100%; + z-index: 101; + font-size: 13px; +} + +.tabs2 +{ + font-size: 10px; +} + +.tabs3 { font-size: 9px; } + +.tablist +{ + margin: 0; + padding: 0; + display: table; + width: 100%; +} + +.tablist li +{ + float: left; + display: table-cell; + line-height: 24px; + list-style: none; +} + +.tablist a +{ + color: #00732F; + display: block; + padding: 0 20px 0 10px; +} + +.tabs3 .tablist a { padding: 0 10px; } + +/* Not used. */ +/* .tablist a:hover{} */ + +/** Currently selected tab. */ +.tablist li.current a { font-weight: bold; } + + + +/*********************************************************** + * Unsorted stuff. + **********************************************************/ + +div.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; +} + +p.startli, p.startdd, p.starttd { + margin-top: 2px; +} + +p.endli { + margin-bottom: 0px; +} + +p.enddd { + margin-bottom: 4px; +} + +p.endtd { + margin-bottom: 2px; +} + +/* @end */ + +caption { + font-weight: bold; +} + +span.legend { + font-size: 70%; + text-align: center; +} + +/* +h3.version { + font-size: 90%; + text-align: center; +} +*/ + +div.qindex, div.navtab{ + /*background-color: #EBEFF6; + border: 1px solid #A3B4D7;*/ + text-align: center; + background-color: #F6F6F6; + border-color: #E6E6E6; + border-width: 1px; + border-style: solid; + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + font-size: 16px; +} + +div.qindex, div.navpath { + width: 100%; + line-height: 140%; +} + +div.navtab { + margin-right: 15px; +} + +/* @group Link Styling */ + +a { + color: #00732F; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: #005C26; +} + +a:hover { + text-decoration: underline; +} + +a.qindex { + font-weight: bold; +} + +a.qindexHL { + font-weight: bold; + background-color: #F6F6F6; + color: #ffffff; + border: 1px double #E6E6E6; +} + +.contents a.qindexHL:visited { + color: #ffffff; +} + + +/* @end */ + +/* Letter in the Class Index page */ +div.ah +{ + font-weight: bold; + width: 100%; +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +div.contents { + margin-top: 10px; + margin-left: 8px; + margin-right: 8px; +} + +td.indexkey { + background-color: #F6F6F6; + font-weight: bold; + margin: 2px 0px 2px 0; + padding: 2px 10px; +} + +td.indexvalue { + background-color: #F6F6F6; + padding: 2px 10px; + margin: 2px 0px; +} + +tr.memlist { + background-color: #F6F6F6; +} + +p.formulaDsp { + text-align: center; +} + +img.formulaDsp { + +} + +img.formulaInl { + vertical-align: middle; +} + + + +/* @group Code Colorization */ + +span.keyword { + color: #840000 +} + +span.keywordtype { + color: #604020 +} + +span.keywordflow { + color: #e08000 +} + +span.comment { + color: #800000 +} + +span.preprocessor { + color: #806020 +} + +span.stringliteral { + color: #002080 +} + +span.charliteral { + color: #008080 +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #000000 +} + +span.vhdlkeyword { + color: #700070 +} + +span.vhdllogic { + color: #ff0000 +} + +/* @end */ + +/* +.search { + color: #003399; + font-weight: bold; +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; +} + +input.search { + font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} +*/ + +td.tiny { + font-size: 75%; +} + +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #E6E6E6; +} + +th.dirtab { + background: #EBEFF6; + font-weight: bold; +} + + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; +} + +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background-color: #F6F6F6; + border: none; + margin: 4px; + padding: 1px 0 0 8px; +} + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; + color: #555; +} +/* +.memItemLeft, .memItemRight, .memTemplParams { + border-top: 1px solid #C4CFE5; +} +*/ +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; +} + +.memItemRight { + width: 100%; +} + +.memTemplParams { + color: #4665A2; + white-space: nowrap; +} + +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; +} + +.memnav { + background-color: #F6F6F6; + border: 1px solid #E6E6E6; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} + +.mempage { + width: 100%; +} + +.memitem { + padding: 0; + margin-bottom: 35px; + margin-right: 5px; +} + +.memname { + white-space: nowrap; + font-weight: bold; + margin-left: 6px; +} + +.memproto, dl.reflist dt { + /*border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9;*/ + padding: 6px 0px 6px 0px; + /*color: #253555;*/ + font-weight: bold; + /*text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9);*/ + /* opera specific markup */ + /*box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 8px; + border-top-left-radius: 8px;*/ + /* firefox specific markup */ + /*-moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 8px; + -moz-border-radius-topleft: 8px;*/ + /* webkit specific markup */ + /*-webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 8px; + -webkit-border-top-left-radius: 8px; + background-image:url('nav_f.png'); + background-repeat:repeat-x;*/ + background-color: #F6F6F6; + border-color: #E6E6E6; + border-width: 1px; + border-style: solid; + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; +} + +.memdoc, dl.reflist dd { + /*border-bottom: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9;*/ + padding: 0 5px; + /*background-color: #FBFCFD;*/ + border-top-width: 0; + /* opera specific markup */ + /*border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);*/ + /* firefox specific markup */ + /*-moz-border-radius-bottomleft: 8px; + -moz-border-radius-bottomright: 8px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F7F8FB 95%, #EEF1F7);*/ + /* webkit specific markup */ + /*-webkit-border-bottom-left-radius: 8px; + -webkit-border-bottom-right-radius: 8px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F7F8FB), to(#EEF1F7));*/ +} + +dl.reflist dt { + padding: 5px; +} + +dl.reflist dd { + margin: 0px 0px 10px 0px; + padding: 5px; +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; +} + +.paramname { + color: #602020; + white-space: nowrap; +} +.paramname em { + font-style: normal; +} + +.params, .retval, .exception, .tparams { + border-spacing: 6px 2px; +} + +.params .paramname, .retval .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir { + font-family: "courier new",courier,monospace; + vertical-align: top; +} + + + + +/* @end */ + +/* @group Directory (tree) */ + +/* for the tree view */ + +.ftvtree { + /*font-family: sans-serif;*/ + margin: 0px; +} + +/* these are for tree view when used as main index */ + +.directory { + font-size: 9pt; + font-weight: bold; + margin: 5px; +} + +.directory h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +/* +The following two styles can be used to replace the root node title +with an image of your choice. Simply uncomment the next two styles, +specify the name of your image and be sure to set 'height' to the +proper pixel height of your image. +*/ + +/* +.directory h3.swap { + height: 61px; + background-repeat: no-repeat; + background-image: url("yourimage.gif"); +} +.directory h3.swap span { + display: none; +} +*/ + +.directory > h3 { + margin-top: 0; +} + +.directory p { + margin: 0px; + white-space: nowrap; +} + +.directory div { + display: none; + margin: 0px; +} + +.directory img { + vertical-align: -30%; +} + +/* these are for tree view when not used as main index */ + +.directory-alt { + font-size: 100%; + font-weight: bold; +} + +.directory-alt h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +.directory-alt > h3 { + margin-top: 0; +} + +.directory-alt p { + margin: 0px; + white-space: nowrap; +} + +.directory-alt div { + display: none; + margin: 0px; +} + +.directory-alt img { + vertical-align: -30%; +} + +/* @end */ + +div.dynheader { + margin-top: 8px; +} + +table.doxtable { + border-collapse:collapse; +} + +table.doxtable td, table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; +} + +table.fieldtable { + width: 100%; + margin-bottom: 10px; + border: 1px solid #A8B8D9; + border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); +} + +.fieldtable td, .fieldtable th { + padding: 3px 7px 2px; +} + +.fieldtable td.fieldtype, .fieldtable td.fieldname { + white-space: nowrap; + border-right: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + vertical-align: top; +} + +.fieldtable td.fielddoc { + border-bottom: 1px solid #A8B8D9; + width: 100%; +} + +.fieldtable tr:last-child td { + border-bottom: none; +} + +.fieldtable th { + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + font-size: 90%; + color: #253555; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #A8B8D9; +} + + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul +{ + font-size: 11px; + height:30px; + line-height:30px; + border-top: 1px solid #C4C4C4; + overflow:hidden; + margin:0px; + padding:0px; +} + +/** TODO: use image as marker. */ +.navpath li +{ + list-style-type:disc; + float:left; + padding-left:5px; + padding-right:5px; + margin-right: 25px; +} + +.navpath li.navelem a +{ + height:32px; + display:block; + text-decoration: none; + outline: none; +} + +.navpath li.navelem a:hover +{ + text-decoration: underline; +} + +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-bottom: 6px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + font-size: 8pt; +} + + +div.summary +{ + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a +{ + white-space: nowrap; +} + +div.ingroups +{ + margin-left: 5px; + font-size: 8pt; + padding-left: 5px; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + +div.header +{ + /*background-image:url('nav_h.png'); + background-repeat:repeat-x; + background-color: #F9FAFC;*/ + margin-top: 10px; + /*border-bottom: 1px solid #C4CFE5;*/ +} + +div.headertitle +{ + padding: 5px 5px 5px 7px; +} + + +dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug +{ + border-left:4px solid; + padding: 0 0 0 6px; +} + +dl.note +{ + border-color: #D0C000; +} + +dl.warning, dl.attention +{ + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant +{ + border-color: #00D000; +} + +dl.deprecated +{ + border-color: #505050; +} + +dl.todo +{ + border-color: #00C0E0; +} + +dl.test +{ + border-color: #3030E0; +} + +dl.bug +{ + border-color: #C08050; +} + +.title +{ + font-size: 150%; + font-weight: bold; + margin: 10px 2px; +} + +.image +{ + text-align: center; +} + +.dotgraph +{ + text-align: center; +} + +.mscgraph +{ + text-align: center; +} + +.caption +{ + font-weight: bold; +} + +div.zoom +{ + border: 1px solid #E6E6E6; +} + +dl.citelist { + margin-bottom:50px; +} + +dl.citelist dt { + color:#334975; + float:left; + font-weight:bold; + margin-right:10px; + padding:5px; +} + +dl.citelist dd { + margin:2px 0; + padding:5px 0; +} + +@media print +{ + #top { display: none; } + #side-nav { display: none; } + #nav-path { display: none; } + body { overflow:visible; } + h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } + .summary { display: none; } + .memitem { page-break-inside: avoid; } + #doc-content + { + margin-left:0 !important; + height:auto !important; + width:auto !important; + overflow:inherit; + display:inline; + } + pre.fragment + { + overflow: visible; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; /* Moz */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: pre-wrap; /* CSS3 */ + word-wrap: break-word; /* IE 5.5+ */ + } +} + diff --git a/doxygen/doxygen-qmi-style/search/search.css b/doxygen/doxygen-qmi-style/search/search.css new file mode 100644 index 0000000..e5d5064 --- /dev/null +++ b/doxygen/doxygen-qmi-style/search/search.css @@ -0,0 +1,238 @@ +/*---------------- Search Box */ + +#FSearchBox { + float: left; +} + +#MSearchBox { + white-space : nowrap; + position: absolute; + float: none; + display: inline; + margin-top: 3px; + right: 0px; + width: 170px; + z-index: 102; +} + +#MSearchBox .left +{ + display:block; + position:absolute; + left:10px; + width:20px; + height:19px; + background:url('search_l.png') no-repeat; + background-position:right; +} + +#MSearchSelect { + display:block; + position:absolute; + width:20px; + height:19px; +} + +.left #MSearchSelect { + left:4px; +} + +.right #MSearchSelect { + right:5px; +} + +#MSearchField { + display:block; + position:absolute; + padding:0; + margin:0; + height:19px; + background:url('search_m.png') repeat-x; + border:none; + width:116px; + margin-left:20px; + padding-left:4px; + color:#909090; + outline:none; + font:9pt Arial, Verdana, sans-serif; +} + +#FSearchBox #MSearchField { + margin-left:15px; +} + +#MSearchBox .right { + display:block; + position:absolute; + right:10px; + width:20px; + height:19px; + background:url('search_r.png') no-repeat; + background-position:left; +} + +#MSearchClose { + display: none; + position: absolute; + top: 4px; + background : none; + border: none; + margin: 0px 4px 0px 0px; + padding: 0px 0px; + outline: none; +} + +.left #MSearchClose { + left: 6px; +} + +.right #MSearchClose { + right: 2px; +} + +.MSearchBoxActive #MSearchField { + color: #000000; +} + +/*---------------- Search filter selection */ + +#MSearchSelectWindow { + display: none; + position: absolute; + left: 0; top: 0; + border: 1px solid #E6E6E6; + background-color: #F6F6F6; + z-index: 1; + padding-top: 4px; + padding-bottom: 4px; + -moz-border-radius: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); +} + +.SelectItem { + font: 8pt Arial, Verdana, sans-serif; + padding-left: 2px; + padding-right: 12px; + border: 0px; +} + +span.SelectionMark { + margin-right: 4px; + font-family: monospace; + outline-style: none; + text-decoration: none; +} + +a.SelectItem { + display: block; + outline-style: none; + color: #000000; + text-decoration: none; + padding-left: 6px; + padding-right: 12px; +} + +a.SelectItem:focus, +a.SelectItem:active { + color: #000000; + outline-style: none; + text-decoration: none; +} + +a.SelectItem:hover { + color: #FFFFFF; + background-color: #B0B0B0; + outline-style: none; + text-decoration: none; + cursor: pointer; + display: block; +} + +/*---------------- Search results window */ + +iframe#MSearchResults { + width: 60ex; + height: 15em; +} + +#MSearchResultsWindow { + display: none; + position: absolute; + left: 0; top: 0; + border: 1px solid #8A8A8A; + background-color: #F6F6F6; +} + +/* ----------------------------------- */ + + +#SRIndex { + clear:both; + padding-bottom: 15px; +} + +.SREntry { + font-size: 10pt; + padding-left: 1ex; +} + +.SRPage .SREntry { + font-size: 8pt; + padding: 1px 5px; +} + +body.SRPage { + margin: 5px 2px; +} + +.SRChildren { + padding-left: 3ex; padding-bottom: .5em +} + +.SRPage .SRChildren { + display: none; +} + +.SRSymbol { + font-weight: bold; + color: #00732F; + font-family: Arial, Verdana, sans-serif; + text-decoration: none; + outline: none; +} + +a.SRScope { + display: block; + color: #00732F; + font-family: Arial, Verdana, sans-serif; + text-decoration: none; + outline: none; +} + +a.SRSymbol:focus, a.SRSymbol:active, +a.SRScope:focus, a.SRScope:active { + text-decoration: underline; +} + +span.SRScope { + padding-left: 4px; +} + +.SRPage .SRStatus { + padding: 2px 5px; + font-size: 8pt; + font-style: italic; +} + +.SRResult { + display: none; +} + +DIV.searchresults { + margin-left: 10px; + margin-right: 10px; +} -- 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(-) 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 e874818889ed799c8f4e4db2adf77dd19013c878 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 20:01:34 +0300 Subject: Cleanup curses better. --- lib/draw/curses.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index c1bb013..0e19538 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -1,10 +1,12 @@ #include "../internal.h" +#define _XOPEN_SOURCE 700 #include #include #include #include #include #include +#include #include /* ncurses.h likes to define stuff for us. @@ -24,6 +26,7 @@ * Dynamically loaded curses API. */ static struct curses { + struct sigaction action; void *handle; WINDOW *stdscr; WINDOW* (*initscr)(void); @@ -87,7 +90,7 @@ static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) static void _bmDrawCursesRender(const bmMenu *menu) { if (!curses.stdscr) { - freopen("/dev/tty", "rw", stdin); + freopen("/dev/tty", "r", stdin); setlocale(LC_CTYPE, ""); if ((curses.stdscr = curses.initscr()) == NULL) return; @@ -134,6 +137,7 @@ static void _bmDrawCursesEndWin(void) curses.endwin(); curses.stdscr = NULL; + freopen("/dev/tty", "w", stdout); } static bmKey _bmDrawCursesGetKey(unsigned int *unicode) @@ -218,9 +222,17 @@ static void _bmDrawCursesFree(void) if (curses.handle) dlclose(curses.handle); + sigaction(SIGABRT, &curses.action, NULL); + sigaction(SIGSEGV, &curses.action, NULL); memset(&curses, 0, sizeof(curses)); } +static void _bmDrawCursesCrashHandler(int sig) +{ + (void)sig; + _bmDrawCursesFree(); +} + int _bmDrawCursesInit(struct _bmRenderApi *api) { memset(&curses, 0, sizeof(curses)); @@ -270,6 +282,11 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) api->render = _bmDrawCursesRender; api->free = _bmDrawCursesFree; + struct sigaction action; + memset(&action, 0, sizeof(struct sigaction)); + action.sa_handler = _bmDrawCursesCrashHandler; + sigaction(SIGABRT, &action, &curses.action); + sigaction(SIGSEGV, &action, &curses.action); return 1; function_pointer_exception: -- cgit v1.2.3-70-g09d2 From 16e023aa3eb495a4b48af1784e5e78dcf4e9ba0b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 20:02:27 +0300 Subject: Assert and whitespace. --- client/client.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/client.c b/client/client.c index 4419967..4bdfdc6 100644 --- a/client/client.c +++ b/client/client.c @@ -63,6 +63,8 @@ static ptrdiff_t getLine(char **outLine, size_t *outAllocated, FILE *stream) static void readItemsToMenuFromStdin(bmMenu *menu) { + assert(menu); + ptrdiff_t len; size_t size = 0; char *line = NULL; @@ -84,7 +86,6 @@ int main(int argc, char **argv) (void)argc, (void)argv; bmMenu *menu = bmMenuNew(BM_DRAW_MODE_CURSES); - if (!menu) return EXIT_FAILURE; @@ -107,7 +108,6 @@ int main(int argc, char **argv) } bmMenuFree(menu); - return (status == BM_RUN_RESULT_SELECTED ? EXIT_SUCCESS : EXIT_FAILURE); } -- 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 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(+) 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(-) 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(+) 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 1f51a67589213a93ddd80843a909b92c2670ddc8 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 20:15:25 +0300 Subject: Don't sort documentation members. --- doxygen/Doxyfile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doxygen/Doxyfile.in b/doxygen/Doxyfile.in index 3b80187..b65debf 100644 --- a/doxygen/Doxyfile.in +++ b/doxygen/Doxyfile.in @@ -526,7 +526,7 @@ INLINE_INFO = YES # name. If set to NO the members will appear in declaration order. # The default value is: YES. -SORT_MEMBER_DOCS = YES +SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member -- 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(-) diff --git a/lib/bemenu.h b/lib/bemenu.h index 35a94e5..4534966 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -239,6 +239,8 @@ int bmMenuSetHighlighted(bmMenu *menu, bmItem *item); /** * Get highlighted item from bmMenu instance. * + * @warning The pointer returned by this function may be invalid after items change. + * * @param menu bmMenu instance from where to get highlighted item. * @return Selected bmItem instance, **NULL** if none highlighted. */ @@ -247,7 +249,7 @@ bmItem* bmMenuGetHighlightedItem(const bmMenu *menu); /** * Set selected items to bmMenu instance. * - * @warning The list won't be copied. + * @warning The list won't be copied, do not free it. * * @param menu bmMenu instance where items will be set. * @param items Array of bmItem pointers to set. @@ -259,6 +261,8 @@ int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int nmemb); /** * Get selected items from bmMenu instance. * + * @warning The pointer returned by this function may be invalid after selection or items change. + * * @param menu bmMenu instance from where to get selected items. * @param outNmemb Reference to unsigned int where total count of returned items will be stored. * @return Pointer to array of bmItem pointers. diff --git a/lib/menu.c b/lib/menu.c index 5f57ae8..bf26c51 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -307,6 +307,8 @@ int bmMenuSetHighlighted(bmMenu *menu, bmItem *item) /** * Get highlighted item from bmMenu instance. * + * @warning The pointer returned by this function may be invalid after items change. + * * @param menu bmMenu instance from where to get highlighted item. * @return Selected bmItem instance, **NULL** if none highlighted. */ @@ -326,7 +328,7 @@ bmItem* bmMenuGetHighlightedItem(const bmMenu *menu) /** * Set selected items to bmMenu instance. * - * @warning The list won't be copied. + * @warning The list won't be copied, do not free it. * * @param menu bmMenu instance where items will be set. * @param items Array of bmItem pointers to set. @@ -342,6 +344,8 @@ int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int nmemb) /** * Get selected items from bmMenu instance. * + * @warning The pointer returned by this function may be invalid after selection or items change. + * * @param menu bmMenu instance from where to get selected items. * @param outNmemb Reference to unsigned int where total count of returned items will be stored. * @return Pointer to array of bmItem pointers. -- cgit v1.2.3-70-g09d2 From 45502a2fd994fdb10b77e4ac79c5f9fd8a8399e8 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 22:02:47 +0300 Subject: Proper filtering functions. --- lib/filter.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++--------- lib/internal.h | 2 + lib/menu.c | 37 ++++++++++++---- lib/util.c | 60 ++++++++++++++++++-------- 4 files changed, 188 insertions(+), 44 deletions(-) diff --git a/lib/filter.c b/lib/filter.c index 4677e73..7e87b11 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -4,37 +4,139 @@ #include /** - * Filter that mimics the vanilla dmenu filtering. + * Shrink bmItem** list pointer. + * + * Useful helper function for filter functions. + * + * @param list Pointer to pointer to list of bmItem pointers. + * @param osize Current size of the list. + * @param nsize New size the list will be shrinked to. + * @return Pointer to list of bmItem pointers. + */ +static bmItem** _bmFilterShrinkList(bmItem ***inOutList, size_t osize, size_t nsize) +{ + assert(inOutList); + + if (nsize == 0) { + free(*inOutList); + return (*inOutList = NULL); + } + + if (nsize >= osize) + return *inOutList; + + void *tmp = malloc(sizeof(bmItem*) * nsize); + if (!tmp) + return *inOutList; + + memcpy(tmp, *inOutList, sizeof(bmItem*) * nsize); + free(*inOutList); + return (*inOutList = tmp); +} + +/** + * Text filter tokenizer helper. + * + * @param menu bmMenu instance which filter to tokenize. + * @param outTokv char pointer reference to list of tokens, this should be freed after use. + * @param outTokc unsigned int reference to number of tokens. + * @return Pointer to buffer that contains tokenized string, this should be freed after use. + */ +static char* _bmFilterTokenize(bmMenu *menu, char ***outTokv, unsigned int *outTokc) +{ + assert(menu); + assert(outTokv); + assert(outTokc); + *outTokv = NULL; + *outTokc = 0; + + char **tokv = NULL, *buffer = NULL; + if (!(buffer = _bmStrdup(menu->filter))) + goto fail; + + char *s, **tmp = NULL; + unsigned int tokc = 0, tokn = 0; + for (s = strtok(buffer, " "); s; tmp[tokc - 1] = s, s = strtok(NULL, " "), tokv = tmp) + if (++tokc > tokn && !(tmp = realloc(tmp, ++tokn * sizeof(char*)))) + goto fail; + + *outTokv = tmp; + *outTokc = tokc; + return buffer; + +fail: + if (buffer) + free(buffer); + if (tokv) + free(tokv); + return NULL; +} + +/** + * Dmenu filterer that accepts substring function. * * @param menu bmMenu instance to filter. + * @param fstrstr Substring function used to match items. * @param outNmemb unsigned int reference to filtered items outNmemb. * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const char *b), unsigned int *outNmemb, unsigned int *outHighlighted) { assert(menu); assert(outNmemb); assert(outHighlighted); *outNmemb = *outHighlighted = 0; - /* FIXME: not real dmenu like filtering at all */ + unsigned int itemsCount; + bmItem **items = bmMenuGetItems(menu, &itemsCount); - bmItem **filtered = calloc(menu->items.count, sizeof(bmItem*)); + bmItem **filtered = calloc(itemsCount, sizeof(bmItem*)); if (!filtered) return NULL; + char **tokv; + unsigned int tokc; + char *buffer = _bmFilterTokenize(menu, &tokv, &tokc); + unsigned int i, f; - for (f = i = 0; i < menu->items.count; ++i) { - bmItem *item = menu->items.list[i]; - if (item->text && strstr(item->text, menu->filter)) { - if (f == 0 || item == bmMenuGetHighlightedItem(menu)) - *outHighlighted = f; - filtered[f++] = item; + for (f = i = 0; i < itemsCount; ++i) { + bmItem *item = items[i]; + if (!item->text && tokc != 0) + continue; + + if (tokc && item->text) { + unsigned int t; + for (t = 0; t < tokc && fstrstr(item->text, tokv[t]); ++t); + if (t < tokc) + continue; } + + if (f == 0 || item == bmMenuGetHighlightedItem(menu)) + *outHighlighted = f; + filtered[f++] = item; } - return _bmShrinkItemList(&filtered, menu->items.count, (*outNmemb = f)); + if (buffer) + free(buffer); + + if (tokv) + free(tokv); + + return _bmFilterShrinkList(&filtered, menu->items.count, (*outNmemb = f)); +} + +/** + * Filter that mimics the vanilla dmenu filtering. + * + * @param menu bmMenu instance to filter. + * @param outNmemb unsigned int reference to filtered items outNmemb. + * @param outHighlighted unsigned int reference to new outHighlighted item index. + * @return Pointer to array of bmItem pointers. + */ +bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) +{ + return _bmFilterDmenuFun(menu, strstr, outNmemb, outHighlighted); } /** @@ -47,14 +149,7 @@ bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outH */ bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) { - assert(menu); - assert(outNmemb); - assert(outHighlighted); - *outNmemb = *outHighlighted = 0; - - /* FIXME: stub */ - - return NULL; + return _bmFilterDmenuFun(menu, _bmStrupstr, outNmemb, outHighlighted); } /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/internal.h b/lib/internal.h index 790b4ed..043c906 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -153,6 +153,8 @@ int _bmItemListRemoveItem(struct _bmItemList *list, const bmItem *item); /* util.c */ char* _bmStrdup(const char *s); +int _bmStrupcmp(const char *hay, const char *needle); +char* _bmStrupstr(const char *hay, const char *needle); bmItem** _bmShrinkItemList(bmItem ***inOutList, size_t osize, size_t nsize); int _bmUtf8StringScreenWidth(const char *string); size_t _bmUtf8RuneNext(const char *string, size_t start); diff --git a/lib/menu.c b/lib/menu.c index bf26c51..e9cf647 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -16,6 +16,11 @@ static void _bmMenuFilter(bmMenu *menu) { assert(menu); + if (!menu->items.list || menu->items.count <= 0) { + _bmItemListFreeList(&menu->filtered); + return; + } + unsigned int count, selected; bmItem **filtered = filterFunc[menu->filterMode](menu, &count, &selected); @@ -82,9 +87,6 @@ void bmMenuFree(bmMenu *menu) if (menu->title) free(menu->title); - if (menu->filtered.list) - free(menu->filtered.list); - bmMenuFreeItems(menu); free(menu); } @@ -200,7 +202,13 @@ const char* bmMenuGetTitle(const bmMenu *menu) int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) { assert(menu); - return _bmItemListAddItemAt(&menu->items, item, index);; + + int ret = _bmItemListAddItemAt(&menu->items, item, index); + + if (ret) + _bmMenuFilter(menu); + + return ret; } /** @@ -212,7 +220,12 @@ int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) */ int bmMenuAddItem(bmMenu *menu, bmItem *item) { - return _bmItemListAddItem(&menu->items, item); + int ret = _bmItemListAddItem(&menu->items, item); + + if (ret) + _bmMenuFilter(menu); + + return ret; } /** @@ -237,6 +250,7 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) if (ret) { _bmItemListRemoveItem(&menu->selection, item); _bmItemListRemoveItem(&menu->filtered, item); + _bmMenuFilter(menu); } return ret; @@ -260,6 +274,7 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) if (ret) { _bmItemListRemoveItem(&menu->selection, item); _bmItemListRemoveItem(&menu->filtered, item); + _bmMenuFilter(menu); } return ret; @@ -275,7 +290,9 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) int bmMenuSetHighlightedIndex(bmMenu *menu, unsigned int index) { assert(menu); - unsigned int itemsCount = (menu->filtered.list ? menu->filtered.count : menu->items.count); + + unsigned int itemsCount; + bmMenuGetFilteredItems(menu, &itemsCount); if (itemsCount <= index) return 0; @@ -376,6 +393,7 @@ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) if (ret) { _bmItemListFreeList(&menu->selection); _bmItemListFreeList(&menu->filtered); + _bmMenuFilter(menu); } return ret; @@ -410,7 +428,7 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); - if (menu->filtered.list) + if (strlen(menu->filter)) return _bmItemListGetItems(&menu->filtered, outNmemb); return _bmItemListGetItems(&menu->items, outNmemb); @@ -463,8 +481,11 @@ bmKey bmMenuGetKey(bmMenu *menu, unsigned int *outUnicode) bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) { assert(menu); + + unsigned int itemsCount; + bmMenuGetFilteredItems(menu, &itemsCount); + char *oldFilter = _bmStrdup(menu->filter); - unsigned int itemsCount = (menu->filtered.list ? menu->filtered.count : menu->items.count); switch (key) { case BM_KEY_LEFT: diff --git a/lib/util.c b/lib/util.c index 5eb980d..06c89cf 100644 --- a/lib/util.c +++ b/lib/util.c @@ -28,30 +28,56 @@ char* _bmStrdup(const char *string) } /** - * Shrink bmItem** list pointer. + * Portable case-insensitive strcmp. * - * Useful helper function for filter functions. + * @param hay C "string" to match against. + * @param needle C "string" to match. + */ +int _bmStrupcmp(const char *hay, const char *needle) +{ + size_t i, len; + + if ((len = strlen(hay)) != strlen(needle)) + return 1; + + for (i = 0; i != len; ++i) + if (toupper(hay[i]) != toupper(needle[i])) + return 1; + + return 0; +} + +/** + * Portable case-insensitive strstr. * - * @param list Pointer to pointer to list of bmItem pointers. - * @param osize Current size of the list. - * @param nsize New size the list will be shrinked to. - * @return Pointer to list of bmItem pointers. + * @param hay C "string" to substring against. + * @param needle C "string" to substring. */ -bmItem** _bmShrinkItemList(bmItem ***inOutList, size_t osize, size_t nsize) +char* _bmStrupstr(const char *hay, const char *needle) { - assert(inOutList); + size_t i, r = 0, p = 0, len, len2; - if (nsize >= osize) - return *inOutList; + if (!_bmStrupcmp(hay, needle)) + return (char*)hay; - void *tmp = malloc(sizeof(bmItem*) * nsize); - if (!tmp) - return *inOutList; + if ((len = strlen(hay)) < (len2 = strlen(needle))) + return NULL; + + for (i = 0; i != len; ++i) { + if (p == len2) + return (char*)hay + r; + + if (toupper(hay[i]) == toupper(needle[p++])) { + if (!r) + r = i; + } else { + if (r) + i = r; + r = p = 0; + } + } - memcpy(tmp, *inOutList, sizeof(bmItem*) * nsize); - free(*inOutList); - *inOutList = tmp; - return *inOutList; + return (p == len2 ? (char*)hay + r : NULL); } /** -- cgit v1.2.3-70-g09d2 From 45e21171593050aedc92b1da44d0441e4a734a1b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 22:15:01 +0300 Subject: Don't run filtering if there is no filter. --- lib/menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/menu.c b/lib/menu.c index e9cf647..acf9ffc 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -16,7 +16,7 @@ static void _bmMenuFilter(bmMenu *menu) { assert(menu); - if (!menu->items.list || menu->items.count <= 0) { + if (!strlen(menu->filter) || !menu->items.list || menu->items.count <= 0) { _bmItemListFreeList(&menu->filtered); return; } -- cgit v1.2.3-70-g09d2 From 71beb7583f6cba2eb8070d5a05dcdf05b54f52bf Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 23:05:13 +0300 Subject: Make it possible filter manually, and optimized filtering. --- lib/bemenu.h | 11 ++++++ lib/filter.c | 27 +++++++++++---- lib/internal.h | 9 +++-- lib/menu.c | 103 ++++++++++++++++++++++++++++++++------------------------- 4 files changed, 96 insertions(+), 54 deletions(-) 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(+) 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 590c5d9f857d594af4146fb6e019228b874b368a Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 23:50:55 +0300 Subject: Undef get_wch --- lib/draw/curses.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 336b21d..a58dd3b 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -12,6 +12,7 @@ /* ncurses.h likes to define stuff for us. * This unforunately mangles with our struct. */ #undef erase +#undef get_wch #undef refresh #undef mvprintw #undef move -- cgit v1.2.3-70-g09d2 From dfdb810099017ae3468ee8db9d3a3da2e21be250 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 00:40:21 +0300 Subject: This is actually better without the count check. --- lib/menu.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/menu.c b/lib/menu.c index cb0d3ee..edbcf73 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -488,12 +488,10 @@ void bmMenuFilter(bmMenu *menu) _bmItemListSetItemsNoCopy(&menu->filtered, filtered, count); menu->index = selected; - if (count) { - if (menu->oldFilter) - free(menu->oldFilter); + if (menu->oldFilter) + free(menu->oldFilter); - menu->oldFilter = _bmStrdup(menu->filter); - } + menu->oldFilter = _bmStrdup(menu->filter); } /** -- cgit v1.2.3-70-g09d2 From 40bd036c5390118a8f1631e1d990cce29edb40f4 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 01:59:05 +0300 Subject: Free the old list. --- lib/list.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/list.c b/lib/list.c index 2123818..fb0bdb2 100644 --- a/lib/list.c +++ b/lib/list.c @@ -79,8 +79,10 @@ int _bmItemListGrow(struct _bmItemList *list, unsigned int step) if (!(tmp = malloc(nsize))) return 0; - if (list->list) + if (list->list) { memcpy(tmp, list->list, sizeof(bmItem*) * list->allocated); + free(list->list); + } } list->list = tmp; -- cgit v1.2.3-70-g09d2 From cc93ff905ff9cf8bcb71a088a16529167511b8ad Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 01:59:50 +0300 Subject: Draw lines properly. --- lib/draw/curses.c | 75 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index a58dd3b..22abfd9 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -47,37 +47,73 @@ static struct curses { int *ESCDELAY; } curses; -static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) +static int _bmDrawCursesResizeBuffer(char **buffer, size_t osize, size_t nsize) { - static int ncols = 0; - static char *buffer = NULL; - int newNcols = curses.getmaxx(curses.stdscr); - - if (newNcols <= 0) - return; + assert(buffer); + assert(nsize); - if (!buffer || newNcols > ncols) { - if (buffer) - free(buffer); + if (nsize == 0 || nsize < osize) + return 0; - ncols = newNcols; + void *tmp; + if (!*buffer || !(tmp = realloc(*buffer, nsize))) { + if (!(tmp = malloc(nsize))) + return 0; - if (!(buffer = calloc(1, ncols + 1))) - return; + if (*buffer) { + memcpy(tmp, *buffer, osize); + free(*buffer); + } } + *buffer = tmp; + memset(*buffer + osize, ' ', (nsize - osize)); + (*buffer)[nsize - 1] = 0; + return 1; +} + +static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) +{ + static int blen = 0; + static char *buffer = NULL; + + int ncols = curses.getmaxx(curses.stdscr); + if (ncols <= 0) + return; + va_list args; va_start(args, format); - int tlen = vsnprintf(NULL, 0, format, args) + 1; - if (tlen > ncols) - tlen = ncols; + int nlen = vsnprintf(NULL, 0, format, args) + 1; + if (nlen < ncols) + nlen = ncols; va_end(args); + if ((!buffer || nlen > blen) && !_bmDrawCursesResizeBuffer(&buffer, blen, nlen)) + return; + + blen = nlen; va_start(args, format); - vsnprintf(buffer, tlen, format, args); + int slen = vsnprintf(buffer, blen - 1, format, args); + memset(buffer + slen, ' ', (blen - slen)); + buffer[blen - 1] = 0; va_end(args); - memset(buffer + tlen - 1, ' ', ncols - tlen + 1); + int dw = 0, i = 0; + while (dw < ncols && i < blen) { + if (buffer[i] == '\t') buffer[i] = ' '; + int next = _bmUtf8RuneNext(buffer, i); + dw += _bmUtf8RuneWidth(buffer + i, next); + i += (next ? next : 1); + } + + if (dw < ncols) { + if (!_bmDrawCursesResizeBuffer(&buffer, blen - 1, blen + (ncols - dw) + 1)) + return; + } else if (i < blen) { + int cc = dw - (dw - ncols); + memset(buffer + i - (dw - ncols), ' ', (ncols - cc) + 1); + buffer[i - (dw - ncols) + (ncols - cc) + 1] = 0; + } if (pair > 0) curses.attron(COLOR_PAIR(pair)); @@ -126,7 +162,8 @@ static void _bmDrawCursesRender(const bmMenu *menu) _bmDrawCursesDrawLine(color, cl++, "%s%s", (highlighted ? ">> " : " "), items[i]->text); } - curses.move(0, titleLen + menu->cursesCursor); + unsigned int ncols = curses.getmaxx(curses.stdscr) - titleLen - 1; + curses.move(0, titleLen + (ncols < menu->cursesCursor ? ncols : menu->cursesCursor)); curses.refresh(); } -- cgit v1.2.3-70-g09d2 From 48cf42f0c9f9850659e739602b7e27b614266364 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 02:02:11 +0300 Subject: Buffer grown too much. --- lib/draw/curses.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 22abfd9..034ed6f 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -107,7 +107,7 @@ static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) } if (dw < ncols) { - if (!_bmDrawCursesResizeBuffer(&buffer, blen - 1, blen + (ncols - dw) + 1)) + if (!_bmDrawCursesResizeBuffer(&buffer, blen - 1, blen + (ncols - dw))) return; } else if (i < blen) { int cc = dw - (dw - ncols); -- cgit v1.2.3-70-g09d2 From c82ebb768c39d19df084a61b6d4332f66fd341b9 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 23:09:01 +0300 Subject: Support mac, duplicate stdin/stdout to make piping possible. --- lib/draw/curses.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 034ed6f..83a039b 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -8,6 +8,21 @@ #include #include #include +#include + +#if _WIN32 +static const char *TTY = "CON"; +#else +static const char *TTY = "/dev/tty"; +#endif + +#if __APPLE__ + const char *DL_PATH = "libncurses.5.dylib"; +#elif _WIN32 +# error FIXME: Compile with windows... or use relative path? +#else + const char *DL_PATH = "libncursesw.so.5"; +#endif /* ncurses.h likes to define stuff for us. * This unforunately mangles with our struct. */ @@ -45,6 +60,8 @@ static struct curses { int (*getmaxy)(WINDOW *win); int (*keypad)(WINDOW *win, bool bf); int *ESCDELAY; + int oldStdin; + int oldStdout; } curses; static int _bmDrawCursesResizeBuffer(char **buffer, size_t osize, size_t nsize) @@ -127,8 +144,14 @@ static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) static void _bmDrawCursesRender(const bmMenu *menu) { if (!curses.stdscr) { - freopen("/dev/tty", "r", stdin); + curses.oldStdin = dup(STDIN_FILENO); + curses.oldStdout = dup(STDOUT_FILENO); + + freopen(TTY, "w", stdout); + freopen(TTY, "r", stdin); + setlocale(LC_CTYPE, ""); + if ((curses.stdscr = curses.initscr()) == NULL) return; @@ -169,14 +192,22 @@ static void _bmDrawCursesRender(const bmMenu *menu) static void _bmDrawCursesEndWin(void) { + freopen(TTY, "w", stdout); + if (curses.refresh) curses.refresh(); if (curses.endwin) curses.endwin(); + if (curses.stdscr) { + dup2(curses.oldStdin, STDIN_FILENO); + dup2(curses.oldStdout, STDOUT_FILENO); + close(curses.oldStdin); + close(curses.oldStdout); + } + curses.stdscr = NULL; - freopen("/dev/tty", "w", stdout); } static bmKey _bmDrawCursesGetKey(unsigned int *unicode) @@ -278,9 +309,7 @@ static void _bmDrawCursesCrashHandler(int sig) 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); + curses.handle = dlopen(DL_PATH, RTLD_LAZY); if (!curses.handle) return 0; -- cgit v1.2.3-70-g09d2 From 28a44b9e410f16ad6f532187b99299ec8536d449 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 23:11:54 +0300 Subject: Add cast for conversion when type isn't unsigned. --- lib/draw/curses.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 83a039b..b3bed81 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -218,7 +218,7 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) if (!curses.stdscr) return BM_KEY_NONE; - curses.get_wch(unicode); + curses.get_wch((wint_t*)unicode); switch (*unicode) { case 16: /* C-p */ case KEY_UP: -- cgit v1.2.3-70-g09d2 From 3c6e167578b1842d53219c143aa2e6fda22ad482 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 23:58:09 +0300 Subject: Silly me, why not just get the highlighted item once.. --- lib/filter.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/filter.c b/lib/filter.c index c8cbfff..3dd2ba4 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -106,7 +106,8 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c unsigned int tokc; char *buffer = _bmFilterTokenize(menu, &tokv, &tokc); - char found = 0; + bmItem *highlighted = bmMenuGetHighlightedItem(menu); + unsigned int i, f; for (f = i = 0; i < itemsCount; ++i) { bmItem *item = items[i]; @@ -120,10 +121,8 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c continue; } - if (!found && item == bmMenuGetHighlightedItem(menu)) { + if (item == highlighted) *outHighlighted = f; - found = 1; - } filtered[f++] = item; } -- cgit v1.2.3-70-g09d2 From 5a0a2659edbaaede9bed08c431013230a0e11122 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 23:58:36 +0300 Subject: Cleanup dmenu filter. --- lib/filter.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/filter.c b/lib/filter.c index 3dd2ba4..85a0037 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -98,13 +98,15 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c items = bmMenuGetItems(menu, &itemsCount); } + char *buffer = NULL; bmItem **filtered = calloc(itemsCount, sizeof(bmItem*)); if (!filtered) - return NULL; + goto fail; char **tokv; unsigned int tokc; - char *buffer = _bmFilterTokenize(menu, &tokv, &tokc); + if (!(buffer = _bmFilterTokenize(menu, &tokv, &tokc))) + goto fail; bmItem *highlighted = bmMenuGetHighlightedItem(menu); @@ -129,11 +131,17 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c if (buffer) free(buffer); - if (tokv) free(tokv); return _bmFilterShrinkList(&filtered, menu->items.count, (*outNmemb = f)); + +fail: + if (filtered) + free(filtered); + if (buffer) + free(buffer); + return NULL; } /** -- cgit v1.2.3-70-g09d2 From 46486abc64fc84a7439edc59e0f5b8b5459b3f99 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 00:00:39 +0300 Subject: Delete is emitted on mac terminal. --- lib/draw/curses.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index b3bed81..3b0e610 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -251,6 +251,7 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) return BM_KEY_PAGE_DOWN; case 8: /* C-h */ + case 127: /* Delete */ case KEY_BACKSPACE: return BM_KEY_BACKSPACE; -- cgit v1.2.3-70-g09d2 From 29c34b0861ff1e5b16831f9093863934cd429e7a Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 00:01:06 +0300 Subject: Consistency. --- lib/draw/curses.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 3b0e610..30bbd8f 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -244,10 +244,10 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) case KEY_END: return BM_KEY_END; - case KEY_PPAGE: /* PAGE UP */ + case KEY_PPAGE: /* Page up */ return BM_KEY_PAGE_UP; - case KEY_NPAGE: /* PAGE DOWN */ + case KEY_NPAGE: /* Page down */ return BM_KEY_PAGE_DOWN; case 8: /* C-h */ -- cgit v1.2.3-70-g09d2 From 058d8a98edb99caa538a922ac13f0f9fab5fb7fb Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 11:15:22 +0300 Subject: Try ncursesw and then ncruses as fallback. --- lib/draw/curses.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 30bbd8f..cb1d204 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -17,11 +17,19 @@ static const char *TTY = "/dev/tty"; #endif #if __APPLE__ - const char *DL_PATH = "libncurses.5.dylib"; + const char *DL_PATH[] = { + "libncursesw.5.dylib", + "libncurses.5.dylib", + NULL + }; #elif _WIN32 # error FIXME: Compile with windows... or use relative path? #else - const char *DL_PATH = "libncursesw.so.5"; + const char *DL_PATH[] = { + "libncursesw.so.5", + "libncurses.so.5", + NULL + }; #endif /* ncurses.h likes to define stuff for us. @@ -310,7 +318,10 @@ static void _bmDrawCursesCrashHandler(int sig) int _bmDrawCursesInit(struct _bmRenderApi *api) { memset(&curses, 0, sizeof(curses)); - curses.handle = dlopen(DL_PATH, RTLD_LAZY); + + unsigned int i; + for (i = 0; DL_PATH[i] && !curses.handle; ++i) + curses.handle = dlopen(DL_PATH[i], RTLD_LAZY); if (!curses.handle) return 0; -- cgit v1.2.3-70-g09d2 From 471046d1b2f9ee0a7f6ac3814a261ef67e218976 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 12:55:05 +0300 Subject: Make comparator functions follow standard more. --- lib/internal.h | 1 + lib/util.c | 41 ++++++++++++++++++++++++++++++----------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/lib/internal.h b/lib/internal.h index 985d366..c33c441 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -159,6 +159,7 @@ int _bmItemListRemoveItem(struct _bmItemList *list, const bmItem *item); /* util.c */ char* _bmStrdup(const char *s); int _bmStrupcmp(const char *hay, const char *needle); +int _bmStrnupcmp(const char *hay, const char *needle, size_t len); char* _bmStrupstr(const char *hay, const char *needle); bmItem** _bmShrinkItemList(bmItem ***inOutList, size_t osize, size_t nsize); int _bmUtf8StringScreenWidth(const char *string); diff --git a/lib/util.c b/lib/util.c index 06c89cf..a77e2ff 100644 --- a/lib/util.c +++ b/lib/util.c @@ -32,19 +32,38 @@ char* _bmStrdup(const char *string) * * @param hay C "string" to match against. * @param needle C "string" to match. + * @return Less than, equal to or greater than zero if hay is lexicographically less than, equal to or greater than needle. */ int _bmStrupcmp(const char *hay, const char *needle) { - size_t i, len; + size_t len, len2; - if ((len = strlen(hay)) != strlen(needle)) - return 1; + if ((len = strlen(hay)) != (len2 = strlen(needle))) + return hay[len] - needle[len2]; - for (i = 0; i != len; ++i) - if (toupper(hay[i]) != toupper(needle[i])) - return 1; + return _bmStrnupcmp(hay, needle, len); +} + +/** + * Portable case-insensitive strncmp. + * + * @param hay C "string" to match against. + * @param needle C "string" to match. + * @return Less than, equal to or greater than zero if hay is lexicographically less than, equal to or greater than needle. + */ +int _bmStrnupcmp(const char *hay, const char *needle, size_t len) +{ + size_t i = 0; + unsigned char a = 0, b = 0; + + const unsigned char *p1 = (const unsigned char*)hay; + const unsigned char *p2 = (const unsigned char*)needle; - return 0; + for (i = 0; len > 0; --len, ++i) + if ((a = toupper(*p1++)) != (b = toupper(*p2++))) + return a - b; + + return a - b; } /** @@ -57,13 +76,13 @@ char* _bmStrupstr(const char *hay, const char *needle) { size_t i, r = 0, p = 0, len, len2; - if (!_bmStrupcmp(hay, needle)) - return (char*)hay; - if ((len = strlen(hay)) < (len2 = strlen(needle))) return NULL; - for (i = 0; i != len; ++i) { + if (!_bmStrnupcmp(hay, needle, len2)) + return (char*)hay; + + for (i = 0; i < len; ++i) { if (p == len2) return (char*)hay + r; -- cgit v1.2.3-70-g09d2 From 9d867bea0c0fdd620d258ecf30e30b40e5dbe3b1 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 12:56:34 +0300 Subject: Sort results like dmenu does. --- lib/filter.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/filter.c b/lib/filter.c index 85a0037..16e57a3 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -82,9 +82,11 @@ fail: * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const char *a, const char *b), unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const char *a, const char *b), int (*fstrncmp)(const char *a, const char *b, size_t len), unsigned int *outNmemb, unsigned int *outHighlighted) { assert(menu); + assert(fstrstr); + assert(fstrncmp); assert(outNmemb); assert(outHighlighted); *outNmemb = *outHighlighted = 0; @@ -110,8 +112,9 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c bmItem *highlighted = bmMenuGetHighlightedItem(menu); - unsigned int i, f; - for (f = i = 0; i < itemsCount; ++i) { + size_t len = (tokc ? strlen(tokv[0]) : 0); + unsigned int i, f, e; + for (e = f = i = 0; i < itemsCount; ++i) { bmItem *item = items[i]; if (!item->text && tokc != 0) continue; @@ -126,7 +129,19 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c if (item == highlighted) *outHighlighted = f; - filtered[f++] = item; + if (tokc && item->text && !fstrncmp(tokv[0], item->text, len + 1)) { /* exact matches */ + //memmove(&filtered[e + 1], &filtered[e], (f - e) * sizeof(bmItem*)); + memmove(&filtered[1], filtered, f * sizeof(bmItem*)); + filtered[0] = item; + e++; /* where do exact matches end */ + } else if (tokc && item->text && !fstrncmp(tokv[0], item->text, len)) { /* prefixes */ + memmove(&filtered[e + 1], &filtered[e], (f - e) * sizeof(bmItem*)); + filtered[e] = item; + e++; /* where do exact matches end */ + } else { + filtered[f] = item; + } + f++; /* where do all matches end */ } if (buffer) @@ -155,7 +170,7 @@ fail: */ bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) { - return _bmFilterDmenuFun(menu, addition, strstr, outNmemb, outHighlighted); + return _bmFilterDmenuFun(menu, addition, strstr, strncmp, outNmemb, outHighlighted); } /** @@ -169,7 +184,7 @@ bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, uns */ bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) { - return _bmFilterDmenuFun(menu, addition, _bmStrupstr, outNmemb, outHighlighted); + return _bmFilterDmenuFun(menu, addition, _bmStrupstr, _bmStrnupcmp, outNmemb, outHighlighted); } /* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From f2608c3c64cb72e1e315142076a7c356facad7de Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 14:15:37 +0300 Subject: Handle empty items. --- client/client.c | 6 ++++-- lib/draw/curses.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client/client.c b/client/client.c index ecaed25..ff32d1b 100644 --- a/client/client.c +++ b/client/client.c @@ -103,8 +103,10 @@ int main(int argc, char **argv) if (status == BM_RUN_RESULT_SELECTED) { unsigned int i, count; bmItem **items = bmMenuGetSelectedItems(menu, &count); - for (i = 0; i < count; ++i) - printf("%s\n", bmItemGetText(items[i])); + for (i = 0; i < count; ++i) { + const char *text = bmItemGetText(items[i]); + printf("%s\n", (text ? text : "")); + } } bmMenuFree(menu); diff --git a/lib/draw/curses.c b/lib/draw/curses.c index cb1d204..653a07d 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -190,7 +190,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) for (i = (menu->index / (lines - 1)) * (lines - 1); i < itemsCount && cl < lines; ++i) { 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); + _bmDrawCursesDrawLine(color, cl++, "%s%s", (highlighted ? ">> " : " "), (items[i]->text ? items[i]->text : "(null)")); } unsigned int ncols = curses.getmaxx(curses.stdscr) - titleLen - 1; -- cgit v1.2.3-70-g09d2 From 544b1973c4155ccef20aea77fef8a5e43210a426 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 14:16:25 +0300 Subject: Why is this here? Remove commented memmove. --- lib/filter.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/filter.c b/lib/filter.c index 16e57a3..4674b5d 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -130,7 +130,6 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c *outHighlighted = f; if (tokc && item->text && !fstrncmp(tokv[0], item->text, len + 1)) { /* exact matches */ - //memmove(&filtered[e + 1], &filtered[e], (f - e) * sizeof(bmItem*)); memmove(&filtered[1], filtered, f * sizeof(bmItem*)); filtered[0] = item; e++; /* where do exact matches end */ -- cgit v1.2.3-70-g09d2 From 9e9b671fa3863aecabdee4a9befdef91ef29f6f3 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 14:36:12 +0300 Subject: Remove highlight logic from filters, it does not belong there. Plus it seems dmenu always just highlights first item on filter change. --- lib/filter.c | 21 ++++++--------------- lib/internal.h | 4 ++-- lib/menu.c | 8 ++++---- 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/lib/filter.c b/lib/filter.c index 4674b5d..836ad83 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -79,17 +79,15 @@ fail: * @param addition This will be 1, if filter is same as previous filter with something appended. * @param fstrstr Substring function used to match items. * @param outNmemb unsigned int reference to filtered items outNmemb. - * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const char *a, const char *b), int (*fstrncmp)(const char *a, const char *b, size_t len), unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const char *a, const char *b), int (*fstrncmp)(const char *a, const char *b, size_t len), unsigned int *outNmemb) { assert(menu); assert(fstrstr); assert(fstrncmp); assert(outNmemb); - assert(outHighlighted); - *outNmemb = *outHighlighted = 0; + *outNmemb = 0; unsigned int itemsCount; bmItem **items; @@ -110,8 +108,6 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c if (!(buffer = _bmFilterTokenize(menu, &tokv, &tokc))) goto fail; - bmItem *highlighted = bmMenuGetHighlightedItem(menu); - size_t len = (tokc ? strlen(tokv[0]) : 0); unsigned int i, f, e; for (e = f = i = 0; i < itemsCount; ++i) { @@ -126,9 +122,6 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c continue; } - if (item == highlighted) - *outHighlighted = f; - if (tokc && item->text && !fstrncmp(tokv[0], item->text, len + 1)) { /* exact matches */ memmove(&filtered[1], filtered, f * sizeof(bmItem*)); filtered[0] = item; @@ -164,12 +157,11 @@ fail: * @param menu bmMenu instance to filter. * @param addition This will be 1, if filter is same as previous filter with something appended. * @param outNmemb unsigned int reference to filtered items outNmemb. - * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb) { - return _bmFilterDmenuFun(menu, addition, strstr, strncmp, outNmemb, outHighlighted); + return _bmFilterDmenuFun(menu, addition, strstr, strncmp, outNmemb); } /** @@ -178,12 +170,11 @@ bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, uns * @param menu bmMenu instance to filter. * @param addition This will be 1, if filter is same as previous filter with something appended. * @param outNmemb unsigned int reference to filtered items outNmemb. - * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb) { - return _bmFilterDmenuFun(menu, addition, _bmStrupstr, _bmStrnupcmp, outNmemb, outHighlighted); + return _bmFilterDmenuFun(menu, addition, _bmStrupstr, _bmStrnupcmp, outNmemb); } /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/internal.h b/lib/internal.h index c33c441..2acd6f5 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -141,8 +141,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api); int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item); /* filter.c */ -bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted); -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted); +bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb); +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb); /* list.c */ void _bmItemListFreeList(struct _bmItemList *list); diff --git a/lib/menu.c b/lib/menu.c index edbcf73..0457da8 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -7,7 +7,7 @@ /** * Filter function map. */ -static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) = { +static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, char addition, unsigned int *outNmemb) = { _bmFilterDmenu, /* BM_FILTER_DMENU */ _bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */ }; @@ -482,11 +482,11 @@ void bmMenuFilter(bmMenu *menu) if (menu->oldFilter && !strcmp(menu->filter, menu->oldFilter)) return; - unsigned int count, selected; - bmItem **filtered = filterFunc[menu->filterMode](menu, addition, &count, &selected); + unsigned int count; + bmItem **filtered = filterFunc[menu->filterMode](menu, addition, &count); _bmItemListSetItemsNoCopy(&menu->filtered, filtered, count); - menu->index = selected; + menu->index = 0; if (menu->oldFilter) free(menu->oldFilter); -- cgit v1.2.3-70-g09d2 From fc08cb9cffce6cdb0ca3aa9f77953e86b2cf1290 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 14:38:42 +0300 Subject: Dmenu outputs input text, if no matches were found. --- client/client.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/client.c b/client/client.c index ff32d1b..130d972 100644 --- a/client/client.c +++ b/client/client.c @@ -107,6 +107,9 @@ int main(int argc, char **argv) const char *text = bmItemGetText(items[i]); printf("%s\n", (text ? text : "")); } + + if (!count && bmMenuGetFilter(menu)) + printf("%s\n", bmMenuGetFilter(menu)); } bmMenuFree(menu); -- 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(-) 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(-) 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 042448798d9d2b95ce5ab9ce718b9a16ef0a5fc1 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 18:44:52 +0300 Subject: Add more keybindings to curses interface. --- lib/draw/curses.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 4b467f6..26754be 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -251,10 +251,12 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) return BM_KEY_RIGHT; case 1: /* C-a */ + case 391: /* S-Home */ case KEY_HOME: return BM_KEY_HOME; case 5: /* C-e */ + case 386: /* S-End */ case KEY_END: return BM_KEY_END; @@ -279,6 +281,7 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) case KEY_DC: return BM_KEY_DELETE; + case 383: /* S-Del */ case 21: /* C-u */ return BM_KEY_LINE_DELETE_LEFT; -- cgit v1.2.3-70-g09d2 From cd73a1ba610db7cfa928b37039a1bbdaee88972a Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 19:21:40 +0300 Subject: Read items from stdin nicer (assumes stdin can fit on one buffer) --- client/client.c | 79 +++++++++++++-------------------------------------------- 1 file changed, 18 insertions(+), 61 deletions(-) diff --git a/client/client.c b/client/client.c index 130d972..c031c0b 100644 --- a/client/client.c +++ b/client/client.c @@ -5,80 +5,37 @@ #include #include -static ptrdiff_t getLine(char **outLine, size_t *outAllocated, FILE *stream) +static void readItemsToMenuFromStdin(bmMenu *menu) { - size_t len = 0, allocated; - char *s, *buffer; - - assert(outLine); - assert(outAllocated); - - if (!stream || feof(stream) || ferror(stream)) - return -1; + assert(menu); - allocated = *outAllocated; - buffer = *outLine; + size_t step = 1024, allocated; + char *buffer; - if (!buffer || allocated == 0) { - if (!(buffer = calloc(1, (allocated = 1024) + 1))) - return -1; - } + if (!(buffer = malloc((allocated = step)))) + return; - for (s = buffer;;) { - if (!fgets(s, allocated - (s - buffer), stream)) { - *outAllocated = allocated; - *outLine = buffer; - 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; + size_t read; + while ((read = fread(buffer + (allocated - step), 1, step, stdin)) == step) { + void *tmp; + if (!(tmp = realloc(buffer, (allocated += step)))) { + free(buffer); + return; } + buffer = tmp; } + buffer[allocated - step + read - 1] = 0; - *outAllocated = allocated; - *outLine = buffer; - - if (s[len - 1] == '\n') - s[len - 1] = 0; - - return s - buffer + len; -} - -static void readItemsToMenuFromStdin(bmMenu *menu) -{ - assert(menu); - - ptrdiff_t len; - size_t size = 0; - char *line = NULL; - - while ((len = getLine(&line, &size, stdin)) != -1) { - bmItem *item = bmItemNew((len > 0 ? line : NULL)); + char *s; + for (s = strtok(buffer, "\n"); s; s = strtok(NULL, "\n")) { + bmItem *item = bmItemNew(s); if (!item) break; bmMenuAddItem(menu, item); } - if (line) - free(line); + free(buffer); } int main(int argc, char **argv) -- cgit v1.2.3-70-g09d2 From 311e4b36768a0d4e113ccf6d2256ca95c6621508 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 19:59:21 +0300 Subject: Use strcspn instead of strtok --- client/client.c | 7 +++++-- lib/draw/curses.c | 10 +++++++--- lib/filter.c | 9 +++++++-- lib/internal.h | 1 + lib/util.c | 13 +++++++++++++ 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/client/client.c b/client/client.c index c031c0b..8e670b1 100644 --- a/client/client.c +++ b/client/client.c @@ -26,13 +26,16 @@ static void readItemsToMenuFromStdin(bmMenu *menu) } buffer[allocated - step + read - 1] = 0; - char *s; - for (s = strtok(buffer, "\n"); s; s = strtok(NULL, "\n")) { + size_t pos; + char *s = buffer; + while ((pos = strcspn(s, "\n")) != 0) { + s[pos] = 0; bmItem *item = bmItemNew(s); if (!item) break; bmMenuAddItem(menu, item); + s += pos + 1; } free(buffer); diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 26754be..3b99cd9 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -1,14 +1,18 @@ #include "../internal.h" -#define _XOPEN_SOURCE 700 + +#define _XOPEN_SOURCE 500 +#include /* sigaction */ +#include /* vsnprintf */ +#undef _XOPEN_SOURCE + #include +#include #include #include #include #include #include -#include #include -#include #if _WIN32 static const char *TTY = "CON"; diff --git a/lib/filter.c b/lib/filter.c index 836ad83..811451b 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -54,12 +54,17 @@ static char* _bmFilterTokenize(bmMenu *menu, char ***outTokv, unsigned int *outT if (!(buffer = _bmStrdup(menu->filter))) goto fail; - char *s, **tmp = NULL; + size_t pos = 0; unsigned int tokc = 0, tokn = 0; - for (s = strtok(buffer, " "); s; tmp[tokc - 1] = s, s = strtok(NULL, " "), tokv = tmp) + char *s = buffer, **tmp = NULL; + while ((pos = _bmStripToken(s, " ")) != 0) { if (++tokc > tokn && !(tmp = realloc(tmp, ++tokn * sizeof(char*)))) goto fail; + tmp[tokc - 1] = s; + s += pos + 1; + } + *outTokv = tmp; *outTokc = tokc; return buffer; diff --git a/lib/internal.h b/lib/internal.h index b3ca87c..1ed13b4 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -163,6 +163,7 @@ int _bmItemListRemoveItem(struct _bmItemList *list, const bmItem *item); /* util.c */ char* _bmStrdup(const char *s); +size_t _bmStripToken(char *string, const char *token); int _bmStrupcmp(const char *hay, const char *needle); int _bmStrnupcmp(const char *hay, const char *needle, size_t len); char* _bmStrupstr(const char *hay, const char *needle); diff --git a/lib/util.c b/lib/util.c index a77e2ff..49f9175 100644 --- a/lib/util.c +++ b/lib/util.c @@ -27,6 +27,19 @@ char* _bmStrdup(const char *string) return (char *)memcpy(copy, string, len); } +/** + * Replaces next token in string with '\0' and returns position for the replaced token. + * + * @param string C "string" where token will be replaced. + * @return Position of the replaced token. + */ +size_t _bmStripToken(char *string, const char *token) +{ + size_t len = strcspn(string, token); + string[len] = 0; + return len; +} + /** * Portable case-insensitive strcmp. * -- cgit v1.2.3-70-g09d2 From 57e76dda1ce422fe5a00df8ee0791aef21dcfe28 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 19:59:45 +0300 Subject: Cleanup header includes. --- client/client.c | 1 - lib/util.c | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client/client.c b/client/client.c index 8e670b1..8bc4be1 100644 --- a/client/client.c +++ b/client/client.c @@ -1,7 +1,6 @@ #include #include #include -#include #include #include diff --git a/lib/util.c b/lib/util.c index 49f9175..351bb5f 100644 --- a/lib/util.c +++ b/lib/util.c @@ -1,6 +1,9 @@ #include "internal.h" -#define _XOPEN_SOURCE 700 -#include + +#define _XOPEN_SOURCE +#include /* wcswidth */ +#undef _XOPEN_SOURCE + #include #include #include -- cgit v1.2.3-70-g09d2 From e738ae17728c697d64beb9adb2dfcc921f864120 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 19:59:58 +0300 Subject: Discard unprintable single width characters instead. --- lib/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util.c b/lib/util.c index 351bb5f..c4533ca 100644 --- a/lib/util.c +++ b/lib/util.c @@ -242,7 +242,7 @@ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char if (len + u8len >= bufSize) return 0; - if (u8len == 1 && iscntrl(*rune)) + if (u8len == 1 && !isprint(*rune)) return 0; char *str = string + start; -- cgit v1.2.3-70-g09d2 From d54381f00991669fa4ee4f3a8037f246ca1904f8 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 20:16:33 +0300 Subject: Fix out of bound access, and provide better tokenize api. --- client/client.c | 4 +++- lib/filter.c | 6 +++--- lib/internal.h | 2 +- lib/util.c | 7 ++++++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/client/client.c b/client/client.c index 8bc4be1..2f95d52 100644 --- a/client/client.c +++ b/client/client.c @@ -28,13 +28,15 @@ static void readItemsToMenuFromStdin(bmMenu *menu) size_t pos; char *s = buffer; while ((pos = strcspn(s, "\n")) != 0) { + size_t next = pos + (s[pos] != 0); s[pos] = 0; + bmItem *item = bmItemNew(s); if (!item) break; bmMenuAddItem(menu, item); - s += pos + 1; + s += next; } free(buffer); diff --git a/lib/filter.c b/lib/filter.c index 811451b..204eac1 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -54,15 +54,15 @@ static char* _bmFilterTokenize(bmMenu *menu, char ***outTokv, unsigned int *outT if (!(buffer = _bmStrdup(menu->filter))) goto fail; - size_t pos = 0; + size_t pos = 0, next; unsigned int tokc = 0, tokn = 0; char *s = buffer, **tmp = NULL; - while ((pos = _bmStripToken(s, " ")) != 0) { + while ((pos = _bmStripToken(s, " ", &next)) > 0) { if (++tokc > tokn && !(tmp = realloc(tmp, ++tokn * sizeof(char*)))) goto fail; tmp[tokc - 1] = s; - s += pos + 1; + s += next; } *outTokv = tmp; diff --git a/lib/internal.h b/lib/internal.h index 1ed13b4..6d116a0 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -163,7 +163,7 @@ int _bmItemListRemoveItem(struct _bmItemList *list, const bmItem *item); /* util.c */ char* _bmStrdup(const char *s); -size_t _bmStripToken(char *string, const char *token); +size_t _bmStripToken(char *string, const char *token, size_t *outNext); int _bmStrupcmp(const char *hay, const char *needle); int _bmStrnupcmp(const char *hay, const char *needle, size_t len); char* _bmStrupstr(const char *hay, const char *needle); diff --git a/lib/util.c b/lib/util.c index c4533ca..bcb27cb 100644 --- a/lib/util.c +++ b/lib/util.c @@ -34,11 +34,16 @@ char* _bmStrdup(const char *string) * Replaces next token in string with '\0' and returns position for the replaced token. * * @param string C "string" where token will be replaced. + * @param outNext Reference to position of next delimiter, or 0 if none. * @return Position of the replaced token. */ -size_t _bmStripToken(char *string, const char *token) +size_t _bmStripToken(char *string, const char *token, size_t *outNext) { size_t len = strcspn(string, token); + + if (outNext) + *outNext = len + (string[len] != 0); + string[len] = 0; return len; } -- cgit v1.2.3-70-g09d2 From bfc53136c8fbea59f116d8d0de92b2711f7804c0 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 20:21:40 +0300 Subject: Add print attribute and fix warning it catched. --- lib/draw/curses.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 3b99cd9..656e47d 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -101,6 +101,9 @@ static int _bmDrawCursesResizeBuffer(char **buffer, size_t osize, size_t nsize) return 1; } +#if __GNUC__ +__attribute__((format(printf, 3, 4))) +#endif static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) { static int blen = 0; @@ -178,8 +181,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) const unsigned int lines = curses.getmaxy(curses.stdscr); curses.erase(); - size_t titleLen = (menu->title ? strlen(menu->title) + 1 : 0); - + int titleLen = (menu->title ? strlen(menu->title) + 1 : 0); _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", menu->filter); if (menu->title) { -- cgit v1.2.3-70-g09d2 From 6692f73c5c3e34baa5b5d8e2fdfa524291979317 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 20:51:32 +0300 Subject: Meh, no (null) it's empty if it's empty. --- lib/draw/curses.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 656e47d..946275e 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -196,7 +196,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) for (i = (menu->index / (lines - 1)) * (lines - 1); i < itemsCount && cl < lines; ++i) { 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 ? items[i]->text : "(null)")); + _bmDrawCursesDrawLine(color, cl++, "%s%s", (highlighted ? ">> " : " "), (items[i]->text ? items[i]->text : "")); } unsigned int ncols = curses.getmaxx(curses.stdscr) - titleLen - 1; -- cgit v1.2.3-70-g09d2 From 5599e1aa8ec62788f4a75f0944eb58bbab7035df Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 20:52:01 +0300 Subject: Store tmp to tokv in for loop, so we can free it if something fails. --- lib/filter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/filter.c b/lib/filter.c index 204eac1..ad1b937 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -57,8 +57,8 @@ static char* _bmFilterTokenize(bmMenu *menu, char ***outTokv, unsigned int *outT size_t pos = 0, next; unsigned int tokc = 0, tokn = 0; char *s = buffer, **tmp = NULL; - while ((pos = _bmStripToken(s, " ", &next)) > 0) { - if (++tokc > tokn && !(tmp = realloc(tmp, ++tokn * sizeof(char*)))) + for (; (pos = _bmStripToken(s, " ", &next)) > 0; tokv = tmp) { + if (++tokc > tokn && !(tmp = realloc(tokv, ++tokn * sizeof(char*)))) goto fail; tmp[tokc - 1] = s; -- cgit v1.2.3-70-g09d2 From f03e03cdd9c267b9f3c171ba57799d876d80d819 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 20:52:29 +0300 Subject: Make filter to pointer. --- lib/draw/curses.c | 2 +- lib/internal.h | 12 ++++++++---- lib/menu.c | 40 ++++++++++++++++++---------------------- lib/util.c | 41 +++++++++++++++++++++++++++++------------ 4 files changed, 56 insertions(+), 39 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 946275e..3d7871e 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -182,7 +182,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) curses.erase(); int titleLen = (menu->title ? strlen(menu->title) + 1 : 0); - _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", menu->filter); + _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", (menu->filter ? menu->filter : "")); if (menu->title) { curses.attron(COLOR_PAIR(1)); diff --git a/lib/internal.h b/lib/internal.h index 6d116a0..4f6afbe 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -103,15 +103,19 @@ struct _bmMenu { /** * Text used to filter matches. - * XXX: Change this to a pointer? */ - char filter[1024]; + char *filter; /** * Used as optimization. */ char *oldFilter; + /** + * Size of filter buffer + */ + size_t filterSize; + /** * Current byte offset on filter text. */ @@ -173,7 +177,7 @@ size_t _bmUtf8RuneNext(const char *string, size_t start); size_t _bmUtf8RunePrev(const char *string, size_t start); size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len); size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *outRuneWidth); -size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *outRuneWidth); -size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *outRuneWidth); +size_t _bmUtf8RuneInsert(char **string, size_t *bufSize, size_t start, const char *rune, unsigned int u8len, size_t *outRuneWidth); +size_t _bmUnicodeInsert(char **string, size_t *bufSize, size_t start, unsigned int unicode, size_t *outRuneWidth); /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/menu.c b/lib/menu.c index a53451d..f414772 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -71,6 +71,9 @@ void bmMenuFree(bmMenu *menu) if (menu->title) free(menu->title); + if (menu->filter) + free(menu->filter); + if (menu->oldFilter) free(menu->oldFilter); @@ -126,12 +129,11 @@ void bmMenuSetFilter(bmMenu *menu, const char *filter) { assert(menu); - if (!filter) { - memset(menu->filter, 0, sizeof(menu->filter)); - return; - } + if (menu->filter) + free(menu->filter); - strncpy(menu->filter, filter, sizeof(menu->filter)); + menu->filter = (filter ? _bmStrdup(filter) : NULL); + menu->filterSize = (filter ? strlen(filter) : 0); } /** @@ -426,7 +428,7 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); - if (strlen(menu->filter)) + if (menu->filter && strlen(menu->filter)) return _bmItemListGetItems(&menu->filtered, outNmemb); return _bmItemListGetItems(&menu->items, outNmemb); @@ -459,7 +461,7 @@ void bmMenuFilter(bmMenu *menu) assert(menu); char addition = 0; - size_t len = strlen(menu->filter); + size_t len = (menu->filter ? strlen(menu->filter) : 0); if (!len || !menu->items.list || menu->items.count <= 0) { _bmItemListFreeList(&menu->filtered); @@ -561,7 +563,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_END: - menu->cursor = strlen(menu->filter); + menu->cursor = (menu->filter ? strlen(menu->filter) : 0); menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); break; @@ -619,17 +621,17 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) case BM_KEY_WORD_DELETE: { - while (menu->cursor < strlen(menu->filter) && !isspace(menu->filter[menu->cursor])) { + while (menu->cursor < (menu->filter ? strlen(menu->filter) : 0) && !isspace(menu->filter[menu->cursor])) { unsigned int oldCursor = menu->cursor; menu->cursor += _bmUtf8RuneNext(menu->filter, menu->cursor); menu->cursesCursor += _bmUtf8RuneWidth(menu->filter + oldCursor, menu->cursor - oldCursor); } - while (menu->cursor > 0 && isspace(menu->filter[menu->cursor - 1])) { + while (menu->cursor > 0 && menu->filter && isspace(menu->filter[menu->cursor - 1])) { unsigned int oldCursor = menu->cursor; menu->cursor -= _bmUtf8RunePrev(menu->filter, menu->cursor); menu->cursesCursor -= _bmUtf8RuneWidth(menu->filter + menu->cursor, oldCursor - menu->cursor); } - while (menu->cursor > 0 && !isspace(menu->filter[menu->cursor - 1])) { + while (menu->cursor > 0 && menu->filter && !isspace(menu->filter[menu->cursor - 1])) { size_t width; menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); menu->cursesCursor -= width; @@ -640,24 +642,18 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) case BM_KEY_UNICODE: { size_t width; - menu->cursor += _bmUnicodeInsert(menu->filter, sizeof(menu->filter) - 1, menu->cursor, unicode, &width); + menu->cursor += _bmUnicodeInsert(&menu->filter, &menu->filterSize, menu->cursor, unicode, &width); menu->cursesCursor += width; } break; case BM_KEY_TAB: { + const char *text; bmItem *highlighted = bmMenuGetHighlightedItem(menu); - if (highlighted && bmItemGetText(highlighted)) { - const char *text = bmItemGetText(highlighted); - size_t len = strlen(text); - - if (len > sizeof(menu->filter) - 1) - len = sizeof(menu->filter) - 1; - - memset(menu->filter, 0, strlen(menu->filter)); - memcpy(menu->filter, text, len); - menu->cursor = strlen(menu->filter); + if (highlighted && (text = bmItemGetText(highlighted))) { + bmMenuSetFilter(menu, text); + menu->cursor = (menu->filter ? strlen(menu->filter) : 0); menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); } } diff --git a/lib/util.c b/lib/util.c index bcb27cb..ca00514 100644 --- a/lib/util.c +++ b/lib/util.c @@ -228,29 +228,45 @@ size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *outRuneWidth) /** * Insert UTF8 rune to buffer. * - * @param string Null terminated C "string". - * @param bufSize Size of the buffer. + * @param inOutString Reference to buffer. + * @param inOutBufSize Reference to size of the buffer. * @param start Start offset where to insert to. (cursor) * @param rune Buffer to insert to string. * @param u8len Byte length of the rune. * @param outRuneWidth Reference to size_t, return number of columns for inserted rune, or -1 on failure. * @return Number of bytes inserted to buffer. */ -size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *outRuneWidth) +size_t _bmUtf8RuneInsert(char **inOutString, size_t *inOutBufSize, size_t start, const char *rune, unsigned int u8len, size_t *outRuneWidth) { - assert(string); + assert(inOutString); + assert(inOutBufSize); if (outRuneWidth) *outRuneWidth = 0; - size_t len = strlen(string); - if (len + u8len >= bufSize) + size_t len = (*inOutString ? strlen(*inOutString) : 0); + if (!*inOutString && !(*inOutString = calloc(1, (*inOutBufSize = u8len + 1)))) return 0; + if (len + u8len >= *inOutBufSize) { + void *tmp; + if (!(tmp = realloc(*inOutString, (*inOutBufSize * 2)))) { + if (!(tmp = malloc((*inOutBufSize * 2)))) + return 0; + + memcpy(tmp, *inOutString, *inOutBufSize); + free(*inOutString); + } + + memset(tmp + *inOutBufSize, 0, *inOutBufSize); + *inOutString = tmp; + *inOutBufSize *= 2; + } + if (u8len == 1 && !isprint(*rune)) return 0; - char *str = string + start; + char *str = *inOutString + start; memmove(str + u8len, str, len - start); memcpy(str, rune, u8len); @@ -262,16 +278,17 @@ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char /** * Insert unicode character to UTF8 buffer. * - * @param string Null terminated C "string". - * @param bufSize Size of the buffer. + * @param inOutString Reference to buffer. + * @param inOutBufSize Reference to size of the buffer. * @param start Start offset where to insert to. (cursor) * @param unicode Unicode character to insert. * @param outRuneWidth Reference to size_t, return number of columns for inserted rune, or -1 on failure. * @return Number of bytes inserted to buffer. */ -size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *outRuneWidth) +size_t _bmUnicodeInsert(char **inOutString, size_t *inOutBufSize, size_t start, unsigned int unicode, size_t *outRuneWidth) { - assert(string); + assert(inOutString); + assert(inOutBufSize); char u8len = ((unicode < 0x80) ? 1 : ((unicode < 0x800) ? 2 : ((unicode < 0x10000) ? 3 : 4))); char mb[5] = { 0, 0, 0, 0 }; @@ -285,7 +302,7 @@ size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int mb[0] |= (unicode >> (i * 6 - 6)); } - return _bmUtf8RuneInsert(string, bufSize, start, mb, u8len, outRuneWidth); + return _bmUtf8RuneInsert(inOutString, inOutBufSize, start, mb, u8len, outRuneWidth); } /* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From ab54f2bc0796b963ac8181f8f31d720e9feffd98 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 20:55:45 +0300 Subject: Here be dragons comments. --- lib/list.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/list.c b/lib/list.c index fb0bdb2..203adf2 100644 --- a/lib/list.c +++ b/lib/list.c @@ -35,6 +35,7 @@ bmItem** _bmItemListGetItems(const struct _bmItemList *list, unsigned int *outNm return list->list; } +/** !!! Frees the old list, not items !!! */ int _bmItemListSetItemsNoCopy(struct _bmItemList *list, bmItem **items, unsigned int nmemb) { assert(list); @@ -51,6 +52,7 @@ int _bmItemListSetItemsNoCopy(struct _bmItemList *list, bmItem **items, unsigned return 1; } +/** !!! Frees the old items and list !!! */ int _bmItemListSetItems(struct _bmItemList *list, const bmItem **items, unsigned int nmemb) { assert(list); -- 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(-) 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 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: Sat, 12 Apr 2014 22:15:46 +0300 Subject: Add TODO about options. --- client/client.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/client.c b/client/client.c index fb94ea5..13d9073 100644 --- a/client/client.c +++ b/client/client.c @@ -86,6 +86,10 @@ static void parseArgs(int *argc, char **argv[]) { 0, 0, 0, 0 } }; + /* TODO: getopt does not support -sf, -sb etc.. + * Either break the interface and make them --sf, --sb (like they are now), + * or parse them before running getopt.. */ + for (;;) { int opt = getopt_long(*argc, *argv, "hviw:I:p:Ibf:m", opts, NULL); if (opt < 0) -- cgit v1.2.3-70-g09d2 From 3f0f507e4f3bea6c5936be62aeff507ed6c4ae25 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 22:42:37 +0300 Subject: Improve CMakeLists and make stuff installable. --- client/CMakeLists.txt | 8 ++++++-- lib/CMakeLists.txt | 28 ++++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index aceaaf5..6af4e3e 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,7 +1,7 @@ # Sources SET(CLIENT_SOURCE client.c) -SET(CLIENT_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/../lib") -SET(CLIENT_LIBRARIES bemenu) +SET(CLIENT_INCLUDE ${BEMENU_INCLUDE_DIRS}) +SET(CLIENT_LIBRARIES ${BEMENU_LIBRARIES}) # Warnings IF (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) @@ -20,5 +20,9 @@ ENDIF () INCLUDE_DIRECTORIES(${CLIENT_INCLUDE}) ADD_EXECUTABLE(client ${CLIENT_SOURCE}) TARGET_LINK_LIBRARIES(client ${CLIENT_LIBRARIES}) +SET_TARGET_PROPERTIES(client PROPERTIES OUTPUT_NAME bemenu) + +# Install +INSTALL(TARGETS client DESTINATION bin) # vim: set ts=8 sw=4 tw=0 : diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index bfdb843..a52bc7b 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -8,9 +8,8 @@ SET(BEMENU_SOURCE library.c draw/curses.c ) -SET(BEMENU_INCLUDE) -SET(BEMENU_LIBRARIES) +# Configure CONFIGURE_FILE(version.h.in version.h @ONLY) # Warnings @@ -26,9 +25,30 @@ IF (UNIX AND CMAKE_COMPILER_IS_GNUCC) ENDIF () ENDIF () +# Parse soversion version +STRING(REGEX MATCHALL "[0-9]+" VERSION_COMPONENTS ${BEMENU_VERSION}) +LIST(GET VERSION_COMPONENTS 0 SOVERSION) + # Compile INCLUDE_DIRECTORIES(${BEMENU_INCLUDE} ${CMAKE_CURRENT_BINARY_DIR}) -ADD_LIBRARY(bemenu ${BEMENU_SOURCE}) -TARGET_LINK_LIBRARIES(bemenu ${BEMENU_LIBRARIES} dl) +ADD_LIBRARY(bemenu SHARED ${BEMENU_SOURCE}) +SET_TARGET_PROPERTIES(bemenu PROPERTIES + VERSION ${BEMENU_VERSION} + SOVERSION ${SOVERSION}) +TARGET_LINK_LIBRARIES(bemenu dl) + +# Install +INSTALL(TARGETS bemenu DESTINATION lib) +INSTALL(FILES bemenu.h DESTINATION include) + +# Unexport +SET(BEMENU_INCLUDES) +SET(BEMENU_INCLUDE_DIRS) +SET(BEMENU_LIBRARIES) + +# Export +SET(BEMENU_INCLUDES "bemenu.h" CACHE STRING "bemenu includes exported from CMake") +SET(BEMENU_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} CACHE STRING "bemenu public header include path exported from CMake") +SET(BEMENU_LIBRARIES "bemenu" "dl" CACHE STRING "bemenu libraries exported from CMake") # vim: set ts=8 sw=4 tw=0 : -- cgit v1.2.3-70-g09d2 From 994d6074bc390492764adb51dc2235d245145afa Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 23:09:35 +0300 Subject: Stop assuming filter is static array anymore. --- lib/menu.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/menu.c b/lib/menu.c index e31c25f..5d2b37b 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -547,7 +547,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) switch (key) { case BM_KEY_LEFT: - { + if (menu->filter) { unsigned int oldCursor = menu->cursor; menu->cursor -= _bmUtf8RunePrev(menu->filter, menu->cursor); menu->cursesCursor -= _bmUtf8RuneWidth(menu->filter + menu->cursor, oldCursor - menu->cursor); @@ -555,7 +555,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_RIGHT: - { + if (menu->filter) { unsigned int oldCursor = menu->cursor; menu->cursor += _bmUtf8RuneNext(menu->filter, menu->cursor); menu->cursesCursor += _bmUtf8RuneWidth(menu->filter + oldCursor, menu->cursor - oldCursor); @@ -568,7 +568,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) case BM_KEY_END: menu->cursor = (menu->filter ? strlen(menu->filter) : 0); - menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); + menu->cursesCursor = (menu->filter ? _bmUtf8StringScreenWidth(menu->filter) : 0); break; case BM_KEY_UP: @@ -598,7 +598,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_BACKSPACE: - { + if (menu->filter) { size_t width; menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); menu->cursesCursor -= width; @@ -606,11 +606,12 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_DELETE: - _bmUtf8RuneRemove(menu->filter, menu->cursor + 1, NULL); + if (menu->filter) + _bmUtf8RuneRemove(menu->filter, menu->cursor + 1, NULL); break; case BM_KEY_LINE_DELETE_LEFT: - { + if (menu->filter) { while (menu->cursor > 0) { size_t width; menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); @@ -620,22 +621,23 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_LINE_DELETE_RIGHT: - menu->filter[menu->cursor] = 0; + if (menu->filter) + menu->filter[menu->cursor] = 0; break; case BM_KEY_WORD_DELETE: - { - while (menu->cursor < (menu->filter ? strlen(menu->filter) : 0) && !isspace(menu->filter[menu->cursor])) { + if (menu->filter) { + while (menu->cursor < strlen(menu->filter) && !isspace(menu->filter[menu->cursor])) { unsigned int oldCursor = menu->cursor; menu->cursor += _bmUtf8RuneNext(menu->filter, menu->cursor); menu->cursesCursor += _bmUtf8RuneWidth(menu->filter + oldCursor, menu->cursor - oldCursor); } - while (menu->cursor > 0 && menu->filter && isspace(menu->filter[menu->cursor - 1])) { + while (menu->cursor > 0 && isspace(menu->filter[menu->cursor - 1])) { unsigned int oldCursor = menu->cursor; menu->cursor -= _bmUtf8RunePrev(menu->filter, menu->cursor); menu->cursesCursor -= _bmUtf8RuneWidth(menu->filter + menu->cursor, oldCursor - menu->cursor); } - while (menu->cursor > 0 && menu->filter && !isspace(menu->filter[menu->cursor - 1])) { + while (menu->cursor > 0 && !isspace(menu->filter[menu->cursor - 1])) { size_t width; menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); menu->cursesCursor -= width; @@ -658,7 +660,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) if (highlighted && (text = bmItemGetText(highlighted))) { bmMenuSetFilter(menu, text); menu->cursor = (menu->filter ? strlen(menu->filter) : 0); - menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); + menu->cursesCursor = (menu->filter ? _bmUtf8StringScreenWidth(menu->filter) : 0); } } break; -- cgit v1.2.3-70-g09d2 From f722f247fcd2d53c346cc99bba15c0d20a16c652 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 23:20:53 +0300 Subject: Xterm does not feed S+pgup/pgdown but does so for Ctrl combination --- lib/draw/curses.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 3d7871e..17089af 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -272,9 +272,11 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) case KEY_NPAGE: /* Page down */ return BM_KEY_PAGE_DOWN; + case 550: /* C-Page up */ case 398: /* S-Page up */ return BM_KEY_SHIFT_PAGE_UP; + case 545: /* C-Page down */ case 396: /* S-Page down */ return BM_KEY_SHIFT_PAGE_DOWN; -- cgit v1.2.3-70-g09d2 From d22612899a74005fd442d54710c86e677742c7f4 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 23:21:43 +0300 Subject: Curses really does not like tabs. --- lib/util.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/util.c b/lib/util.c index ca00514..fa85431 100644 --- a/lib/util.c +++ b/lib/util.c @@ -130,16 +130,26 @@ int _bmUtf8StringScreenWidth(const char *string) { assert(string); - int num_char = mbstowcs(NULL, string, 0) + 1; + char *mstr = _bmStrdup(string); + if (!mstr) + return strlen(string); + + char *s; + for (s = mstr; *s; ++s) if (*s == '\t') *s = ' '; + + int num_char = mbstowcs(NULL, mstr, 0) + 1; wchar_t *wstring = malloc((num_char + 1) * sizeof (wstring[0])); - if (mbstowcs(wstring, string, num_char) == (size_t)(-1)) { + if (mbstowcs(wstring, mstr, num_char) == (size_t)(-1)) { free(wstring); - return strlen(string); + int len = strlen(mstr); + free(mstr); + return len; } int length = wcswidth(wstring, num_char); free(wstring); + free(mstr); return length; } -- cgit v1.2.3-70-g09d2 From 7da3ebe4239675b18560ab642abd1a100801da66 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 23:58:01 +0300 Subject: Scrollable curses input \o/ --- lib/draw/curses.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 17089af..9e8502b 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -181,8 +181,18 @@ static void _bmDrawCursesRender(const bmMenu *menu) const unsigned int lines = curses.getmaxy(curses.stdscr); curses.erase(); - int titleLen = (menu->title ? strlen(menu->title) + 1 : 0); - _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", (menu->filter ? menu->filter : "")); + unsigned int titleLen = (menu->title ? strlen(menu->title) + 1 : 0); + unsigned int ncols = curses.getmaxx(curses.stdscr); + unsigned int ccols = ncols - titleLen - 1; + unsigned int doffset = (menu->cursesCursor < ccols ? 0 : menu->cursesCursor - ccols); + + if (doffset > 0) { + /* find offset where we won't break the UTF8 string */ + doffset += _bmUtf8RuneNext(menu->filter, doffset); + doffset -= _bmUtf8RunePrev(menu->filter, doffset); + } + + _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", (menu->filter ? menu->filter + doffset : "")); if (menu->title) { curses.attron(COLOR_PAIR(1)); @@ -199,8 +209,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) _bmDrawCursesDrawLine(color, cl++, "%s%s", (highlighted ? ">> " : " "), (items[i]->text ? items[i]->text : "")); } - unsigned int ncols = curses.getmaxx(curses.stdscr) - titleLen - 1; - curses.move(0, titleLen + (ncols < menu->cursesCursor ? ncols : menu->cursesCursor)); + curses.move(0, titleLen + (menu->cursesCursor < ccols ? menu->cursesCursor : ccols)); curses.refresh(); } -- cgit v1.2.3-70-g09d2 From 8e1ff89f9cedab9a5f5b5f6a702f76feda514182 Mon Sep 17 00:00:00 2001 From: Martin Langasek Date: Sat, 12 Apr 2014 23:05:12 +0200 Subject: Fix typo in Mainpage.dox --- doxygen/Mainpage.dox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doxygen/Mainpage.dox b/doxygen/Mainpage.dox index 869071c..1b91ebf 100644 --- a/doxygen/Mainpage.dox +++ b/doxygen/Mainpage.dox @@ -2,7 +2,7 @@ @mainpage Main Page bemenu is a dynamic menu library and client program inspired by dmenu. -You can create flexible menu oriented programs using the library interface in your favorite language that has bemenu bindings avaialable. +You can create flexible menu oriented programs using the library interface in your favorite language that has bemenu bindings available. Unlike old dmenu approach, with library you don't have to feed unneccessary metadata into client program and parse the result. Instead your program will be aware of the items and possible metadata inside the menu. -- cgit v1.2.3-70-g09d2 From 5251ef6ac03e30abf320886d5efe9644bf424ee2 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi 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(-) 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 fc6539c1013d8187ed5a0dc3dfce65f3f626bf79 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 13 Apr 2014 01:22:49 +0300 Subject: Buffer as format is dangerous, this functions seems to be VA one. --- lib/draw/curses.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 9e8502b..f05db01 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -150,7 +150,7 @@ static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) if (pair > 0) curses.attron(COLOR_PAIR(pair)); - curses.mvprintw(y, 0, buffer); + curses.mvprintw(y, 0, "%s", buffer); if (pair > 0) curses.attroff(COLOR_PAIR(pair)); -- cgit v1.2.3-70-g09d2 From 83d67f80c0d1935fff4e36bd63b2ac64c1dd2bc9 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 13 Apr 2014 01:26:53 +0300 Subject: Handle -l option by ignoring it --- client/client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/client.c b/client/client.c index 13d9073..0e6bf15 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:I:p:Ibf:m", opts, NULL); + int opt = getopt_long(*argc, *argv, "hviw:l:I:p:Ibf:m", opts, NULL); if (opt < 0) break; -- cgit v1.2.3-70-g09d2 From 60c1ab88cc4ee7b5d4f87c7867059464757666bb Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 13 Apr 2014 01:39:31 +0300 Subject: Store old actions to seperate actions. --- lib/draw/curses.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index f05db01..3b160a9 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -54,7 +54,8 @@ static const char *TTY = "/dev/tty"; * Dynamically loaded curses API. */ static struct curses { - struct sigaction action; + struct sigaction abrtAction; + struct sigaction segvAction; void *handle; WINDOW *stdscr; WINDOW* (*initscr)(void); @@ -342,8 +343,8 @@ static void _bmDrawCursesFree(void) if (curses.handle) dlclose(curses.handle); - sigaction(SIGABRT, &curses.action, NULL); - sigaction(SIGSEGV, &curses.action, NULL); + sigaction(SIGABRT, &curses.abrtAction, NULL); + sigaction(SIGSEGV, &curses.segvAction, NULL); memset(&curses, 0, sizeof(curses)); } @@ -407,8 +408,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) struct sigaction action; memset(&action, 0, sizeof(struct sigaction)); action.sa_handler = _bmDrawCursesCrashHandler; - sigaction(SIGABRT, &action, &curses.action); - sigaction(SIGSEGV, &action, &curses.action); + sigaction(SIGABRT, &action, &curses.abrtAction); + sigaction(SIGSEGV, &action, &curses.segvAction); return 1; function_pointer_exception: -- cgit v1.2.3-70-g09d2 From 9c0b25f6a9f7e0a0c2f8b6a4608aab68e5e6a909 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 13 Apr 2014 02:47:54 +0300 Subject: Enable noecho and raw mode. --- lib/draw/curses.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 3b160a9..7d91d36 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -72,6 +72,8 @@ static struct curses { int (*getmaxx)(WINDOW *win); int (*getmaxy)(WINDOW *win); int (*keypad)(WINDOW *win, bool bf); + int (*noecho)(void); + int (*raw)(void); int *ESCDELAY; int oldStdin; int oldStdout; @@ -173,6 +175,8 @@ static void _bmDrawCursesRender(const bmMenu *menu) *curses.ESCDELAY = 25; curses.keypad(curses.stdscr, true); + curses.noecho(); + curses.raw(); curses.start_color(); curses.init_pair(1, COLOR_BLACK, COLOR_RED); @@ -395,6 +399,10 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) goto function_pointer_exception; if (!bmLoadFunction(keypad)) goto function_pointer_exception; + if (!bmLoadFunction(noecho)) + goto function_pointer_exception; + if (!bmLoadFunction(raw)) + goto function_pointer_exception; if (!bmLoadFunction(ESCDELAY)) goto function_pointer_exception; -- cgit v1.2.3-70-g09d2 From c2bed7689d1d97e7bfc0ab52e028e290ee0b3f0e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 13 Apr 2014 03:00:34 +0300 Subject: Enable cursor (hidden when launched by vim) --- lib/draw/curses.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 7d91d36..0304dd5 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -72,6 +72,7 @@ static struct curses { int (*getmaxx)(WINDOW *win); int (*getmaxy)(WINDOW *win); int (*keypad)(WINDOW *win, bool bf); + int (*curs_set)(int visibility); int (*noecho)(void); int (*raw)(void); int *ESCDELAY; @@ -175,6 +176,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) *curses.ESCDELAY = 25; curses.keypad(curses.stdscr, true); + curses.curs_set(1); curses.noecho(); curses.raw(); @@ -399,6 +401,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) goto function_pointer_exception; if (!bmLoadFunction(keypad)) goto function_pointer_exception; + if (!bmLoadFunction(curs_set)) + goto function_pointer_exception; if (!bmLoadFunction(noecho)) goto function_pointer_exception; if (!bmLoadFunction(raw)) -- cgit v1.2.3-70-g09d2 From 239259aebf1fe33387c189ad5f938e034fe8a033 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 13 Apr 2014 03:15:18 +0300 Subject: Unfortunately we can't test curses on cronjob --- test/bmMenuNew.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/bmMenuNew.c b/test/bmMenuNew.c index ccb572d..ebab314 100644 --- a/test/bmMenuNew.c +++ b/test/bmMenuNew.c @@ -1,6 +1,8 @@ #include -#include +#include +#include #include +#include int main(int argc, char **argv) { @@ -10,6 +12,10 @@ int main(int argc, char **argv) { bmDrawMode i; for (i = 0; i < BM_DRAW_MODE_LAST; ++i) { + if (i == BM_DRAW_MODE_CURSES && !isatty(STDIN_FILENO)) { + printf("Skipping test for mode BM_DRAW_MODE_CURSES, as not running on terminal.\n"); + continue; + } bmMenu *menu = bmMenuNew(i); assert(menu); bmMenuRender(menu); -- cgit v1.2.3-70-g09d2 From 66a6f08c8a9a90fd8c176a4c6b1f5aa6094233c4 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 13 Apr 2014 03:23:11 +0300 Subject: vsnprintf is not on same headers on darwin. --- lib/draw/curses.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 0304dd5..6ebfb45 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -1,5 +1,11 @@ #include "../internal.h" +#if __APPLE__ +# define _C99_SOURCE +# include /* vsnprintf */ +# undef _C99_SOURCE +#endif + #define _XOPEN_SOURCE 500 #include /* sigaction */ #include /* vsnprintf */ -- cgit v1.2.3-70-g09d2 From 850a8335d5c35cc81ba576cd4636460ea1684223 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 13 Apr 2014 04:16:23 +0300 Subject: Flush input buffer after showing menu first time. --- lib/draw/curses.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 6ebfb45..9a123df 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -79,6 +79,7 @@ static struct curses { int (*getmaxy)(WINDOW *win); int (*keypad)(WINDOW *win, bool bf); int (*curs_set)(int visibility); + int (*flushinp)(void); int (*noecho)(void); int (*raw)(void); int *ESCDELAY; @@ -181,6 +182,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) return; *curses.ESCDELAY = 25; + curses.flushinp(); curses.keypad(curses.stdscr, true); curses.curs_set(1); curses.noecho(); @@ -409,6 +411,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) goto function_pointer_exception; if (!bmLoadFunction(curs_set)) goto function_pointer_exception; + if (!bmLoadFunction(flushinp)) + goto function_pointer_exception; if (!bmLoadFunction(noecho)) goto function_pointer_exception; if (!bmLoadFunction(raw)) -- cgit v1.2.3-70-g09d2 From 0f34b70c90afb487541448ae88ca99d7186e8150 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 18:08:13 +0300 Subject: Move this check before allocation. --- lib/util.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/util.c b/lib/util.c index fa85431..539afa2 100644 --- a/lib/util.c +++ b/lib/util.c @@ -254,6 +254,9 @@ size_t _bmUtf8RuneInsert(char **inOutString, size_t *inOutBufSize, size_t start, if (outRuneWidth) *outRuneWidth = 0; + if (u8len == 1 && !isprint(*rune)) + return 0; + size_t len = (*inOutString ? strlen(*inOutString) : 0); if (!*inOutString && !(*inOutString = calloc(1, (*inOutBufSize = u8len + 1)))) return 0; @@ -273,9 +276,6 @@ size_t _bmUtf8RuneInsert(char **inOutString, size_t *inOutBufSize, size_t start, *inOutBufSize *= 2; } - if (u8len == 1 && !isprint(*rune)) - return 0; - char *str = *inOutString + start; memmove(str + u8len, str, len - start); memcpy(str, rune, u8len); -- cgit v1.2.3-70-g09d2 From f410a5e391deccc10838b363955fe823a336bc82 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 18:10:01 +0300 Subject: Make sure the text is null terminated. --- lib/util.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/util.c b/lib/util.c index 539afa2..2ace7f7 100644 --- a/lib/util.c +++ b/lib/util.c @@ -279,6 +279,7 @@ size_t _bmUtf8RuneInsert(char **inOutString, size_t *inOutBufSize, size_t start, char *str = *inOutString + start; memmove(str + u8len, str, len - start); memcpy(str, rune, u8len); + (*inOutString)[len + u8len] = 0; if (outRuneWidth) *outRuneWidth = _bmUtf8RuneWidth(rune, u8len); -- cgit v1.2.3-70-g09d2 From 066c5c2e61d1b91f04564115ec061968a0716533 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:00:03 +0300 Subject: Make sure text fits tightly terminal + simplified logic --- lib/draw/curses.c | 53 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 9a123df..5cd1335 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -87,12 +87,12 @@ static struct curses { int oldStdout; } curses; -static int _bmDrawCursesResizeBuffer(char **buffer, size_t osize, size_t nsize) +static int _bmDrawCursesResizeBuffer(char **buffer, size_t *osize, size_t nsize) { assert(buffer); - assert(nsize); + assert(osize); - if (nsize == 0 || nsize < osize) + if (nsize == 0 || nsize <= *osize) return 0; void *tmp; @@ -101,14 +101,13 @@ static int _bmDrawCursesResizeBuffer(char **buffer, size_t osize, size_t nsize) return 0; if (*buffer) { - memcpy(tmp, *buffer, osize); + memcpy(tmp, *buffer, *osize); free(*buffer); } } *buffer = tmp; - memset(*buffer + osize, ' ', (nsize - osize)); - (*buffer)[nsize - 1] = 0; + *osize = nsize; return 1; } @@ -117,32 +116,27 @@ __attribute__((format(printf, 3, 4))) #endif static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) { - static int blen = 0; + static size_t blen = 0; static char *buffer = NULL; - int ncols = curses.getmaxx(curses.stdscr); + size_t ncols = curses.getmaxx(curses.stdscr); if (ncols <= 0) return; va_list args; va_start(args, format); - int nlen = vsnprintf(NULL, 0, format, args) + 1; - if (nlen < ncols) - nlen = ncols; + size_t nlen = vsnprintf(NULL, 0, format, args); va_end(args); - if ((!buffer || nlen > blen) && !_bmDrawCursesResizeBuffer(&buffer, blen, nlen)) + if ((!buffer || nlen > blen) && !_bmDrawCursesResizeBuffer(&buffer, &blen, nlen + 1)) return; - blen = nlen; va_start(args, format); - int slen = vsnprintf(buffer, blen - 1, format, args); - memset(buffer + slen, ' ', (blen - slen)); - buffer[blen - 1] = 0; + vsnprintf(buffer, blen - 1, format, args); va_end(args); - int dw = 0, i = 0; - while (dw < ncols && i < blen) { + size_t dw = 0, i = 0; + while (dw < ncols && i < nlen) { if (buffer[i] == '\t') buffer[i] = ' '; int next = _bmUtf8RuneNext(buffer, i); dw += _bmUtf8RuneWidth(buffer + i, next); @@ -150,12 +144,27 @@ static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) } if (dw < ncols) { - if (!_bmDrawCursesResizeBuffer(&buffer, blen - 1, blen + (ncols - dw))) - return; + /* line is too short, widen it */ + size_t offset = i + (ncols - dw); + if (blen <= offset && !_bmDrawCursesResizeBuffer(&buffer, &blen, offset + 1)) + return; + + memset(buffer + nlen, ' ', offset - nlen); + buffer[offset] = 0; } else if (i < blen) { - int cc = dw - (dw - ncols); + /* line is too long, shorten it */ + i -= _bmUtf8RunePrev(buffer, i - (dw - ncols)) - 1; + size_t cc = dw - (dw - ncols); + + size_t offset = i - (dw - ncols) + (ncols - cc) + 1; + if (blen <= offset) { + int diff = offset - blen + 1; + if (!_bmDrawCursesResizeBuffer(&buffer, &blen, blen + diff)) + return; + } + memset(buffer + i - (dw - ncols), ' ', (ncols - cc) + 1); - buffer[i - (dw - ncols) + (ncols - cc) + 1] = 0; + buffer[offset] = 0; } if (pair > 0) -- cgit v1.2.3-70-g09d2 From 5b873de2ded661416d64dc589c1d10bde2c5dfc0 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:00:15 +0300 Subject: Better input scroll logic. --- lib/draw/curses.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 5cd1335..4ae63d7 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -208,12 +208,12 @@ static void _bmDrawCursesRender(const bmMenu *menu) unsigned int titleLen = (menu->title ? strlen(menu->title) + 1 : 0); unsigned int ncols = curses.getmaxx(curses.stdscr); unsigned int ccols = ncols - titleLen - 1; - unsigned int doffset = (menu->cursesCursor < ccols ? 0 : menu->cursesCursor - ccols); + unsigned int dcols = 0, doffset = menu->cursor; - if (doffset > 0) { - /* find offset where we won't break the UTF8 string */ - doffset += _bmUtf8RuneNext(menu->filter, doffset); - doffset -= _bmUtf8RunePrev(menu->filter, doffset); + while (doffset > 0 && dcols < ccols) { + int prev = _bmUtf8RunePrev(menu->filter, doffset); + dcols += _bmUtf8RuneWidth(menu->filter + doffset - prev, prev); + doffset -= (prev ? prev : 1); } _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", (menu->filter ? menu->filter + doffset : "")); -- cgit v1.2.3-70-g09d2 From 9df8716cb8787110aedf5d9ad9c27b4eb3bade39 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:11:26 +0300 Subject: Handle window resize. --- lib/draw/curses.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 4ae63d7..5673c90 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -62,6 +62,7 @@ static const char *TTY = "/dev/tty"; static struct curses { struct sigaction abrtAction; struct sigaction segvAction; + struct sigaction winchAction; void *handle; WINDOW *stdscr; WINDOW* (*initscr)(void); @@ -273,6 +274,11 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) curses.get_wch((wint_t*)unicode); switch (*unicode) { +#if KEY_RESIZE + case KEY_RESIZE: + return BM_KEY_NONE; +#endif + case 16: /* C-p */ case KEY_UP: return BM_KEY_UP; @@ -335,7 +341,6 @@ 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; @@ -368,6 +373,7 @@ static void _bmDrawCursesFree(void) sigaction(SIGABRT, &curses.abrtAction, NULL); sigaction(SIGSEGV, &curses.segvAction, NULL); + sigaction(SIGWINCH, &curses.winchAction, NULL); memset(&curses, 0, sizeof(curses)); } @@ -377,6 +383,16 @@ static void _bmDrawCursesCrashHandler(int sig) _bmDrawCursesFree(); } +static void _bmDrawCursesResizeHandler(int sig) +{ + (void)sig; + if (!curses.stdscr) + return; + + curses.endwin(); + curses.refresh(); +} + int _bmDrawCursesInit(struct _bmRenderApi *api) { memset(&curses, 0, sizeof(curses)); @@ -441,6 +457,9 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) action.sa_handler = _bmDrawCursesCrashHandler; sigaction(SIGABRT, &action, &curses.abrtAction); sigaction(SIGSEGV, &action, &curses.segvAction); + + action.sa_handler = _bmDrawCursesResizeHandler; + sigaction(SIGWINCH, &action, &curses.winchAction); return 1; function_pointer_exception: -- cgit v1.2.3-70-g09d2 From bcda5404011714622ec22a37c4b7837defe4377e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:14:30 +0300 Subject: Hide title if more than NCOLS --- lib/draw/curses.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 5673c90..2cc84af 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -206,8 +206,12 @@ static void _bmDrawCursesRender(const bmMenu *menu) const unsigned int lines = curses.getmaxy(curses.stdscr); curses.erase(); - unsigned int titleLen = (menu->title ? strlen(menu->title) + 1 : 0); unsigned int ncols = curses.getmaxx(curses.stdscr); + unsigned int titleLen = (menu->title ? strlen(menu->title) + 1 : 0); + + if (titleLen >= ncols) + titleLen = 0; + unsigned int ccols = ncols - titleLen - 1; unsigned int dcols = 0, doffset = menu->cursor; @@ -219,7 +223,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", (menu->filter ? menu->filter + doffset : "")); - if (menu->title) { + if (menu->title && titleLen > 0) { curses.attron(COLOR_PAIR(1)); curses.mvprintw(0, 0, menu->title); curses.attroff(COLOR_PAIR(1)); -- 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(-) 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(-) diff --git a/client/client.c b/client/client.c index 0e6bf15..7df00bb 100644 --- a/client/client.c +++ b/client/client.c @@ -91,7 +91,7 @@ static void parseArgs(int *argc, char **argv[]) * or parse them before running getopt.. */ for (;;) { - int opt = getopt_long(*argc, *argv, "hviw:l:I:p:Ibf:m", opts, NULL); + int opt = getopt_long(*argc, *argv, "hviwl:I:p:I:bfm:", opts, NULL); if (opt < 0) break; @@ -190,6 +190,7 @@ int main(int argc, char **argv) bmMenuSetTitle(menu, client.title); bmMenuSetFilterMode(menu, client.filterMode); + bmMenuSetWrap(menu, client.wrap); readItemsToMenuFromStdin(menu); diff --git a/lib/bemenu.h b/lib/bemenu.h index 172c6ed..5457cca 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -202,6 +202,22 @@ void bmMenuSetFilterMode(bmMenu *menu, bmFilterMode mode); */ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu); +/** + * Set selection wrapping on/off. + * + * @param menu bmMenu instance where to toggle selection wrapping. + * @param int 1 == on, 0 == off. + */ +void bmMenuSetWrap(bmMenu *menu, int wrap); + +/** + * Get selection wrapping state. + * + * @param menu bmMenu instance where to get selection wrapping state. + * @return int for wrap state. + */ +int bmMenuGetWrap(const bmMenu *menu); + /** * Set title to bmMenu instance. * diff --git a/lib/internal.h b/lib/internal.h index 4f6afbe..2fc9393 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -141,6 +141,11 @@ struct _bmMenu { * Drawing mode used in menu instance. */ bmDrawMode drawMode; + + /** + * Should selection be wrapped? + */ + char wrap; }; /* draw/curses.c */ diff --git a/lib/menu.c b/lib/menu.c index 5d2b37b..d949430 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -172,6 +172,30 @@ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu) return menu->filterMode; } +/** + * Set selection wrapping on/off. + * + * @param menu bmMenu instance where to toggle selection wrapping. + * @param int 1 == on, 0 == off. + */ +void bmMenuSetWrap(bmMenu *menu, int wrap) +{ + assert(menu); + menu->wrap = (wrap ? 1 : 0); +} + +/** + * Get selection wrapping state. + * + * @param menu bmMenu instance where to get selection wrapping state. + * @return int for wrap state. + */ +int bmMenuGetWrap(const bmMenu *menu) +{ + assert(menu); + return menu->wrap; +} + /** * Set title to bmMenu instance. * @@ -572,13 +596,19 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_UP: - if (menu->index > 0) + if (menu->index > 0) { menu->index--; + } else if (menu->wrap) { + menu->index = itemsCount - 1; + } break; case BM_KEY_DOWN: - if (menu->index < itemsCount - 1) + if (menu->index < itemsCount - 1) { menu->index++; + } else if (menu->wrap) { + menu->index = 0; + } break; case BM_KEY_PAGE_UP: -- cgit v1.2.3-70-g09d2 From 7533cebfe531f2264b10903a5d1b8a0d60b9fcae Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:33:50 +0300 Subject: Tell curses to use terminal default colors --- lib/draw/curses.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 2cc84af..f8137b8 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -76,6 +76,7 @@ static struct curses { int (*attroff)(int attrs); int (*attron)(int attrs); int (*start_color)(void); + int (*use_default_colors)(void); int (*getmaxx)(WINDOW *win); int (*getmaxy)(WINDOW *win); int (*keypad)(WINDOW *win, bool bf); @@ -199,8 +200,9 @@ static void _bmDrawCursesRender(const bmMenu *menu) curses.raw(); curses.start_color(); + curses.use_default_colors(); curses.init_pair(1, COLOR_BLACK, COLOR_RED); - curses.init_pair(2, COLOR_RED, COLOR_BLACK); + curses.init_pair(2, COLOR_RED, -1); } const unsigned int lines = curses.getmaxy(curses.stdscr); @@ -432,6 +434,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) goto function_pointer_exception; if (!bmLoadFunction(start_color)) goto function_pointer_exception; + if (!bmLoadFunction(use_default_colors)) + goto function_pointer_exception; if (!bmLoadFunction(getmaxx)) goto function_pointer_exception; if (!bmLoadFunction(getmaxy)) -- cgit v1.2.3-70-g09d2 From 14a164408504ad98d7861fec29979b1be1828c0e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:39:20 +0300 Subject: Useless whitespace. --- client/client.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/client.c b/client/client.c index 7df00bb..df05d34 100644 --- a/client/client.c +++ b/client/client.c @@ -56,10 +56,7 @@ static void usage(FILE *out, const char *name) " -nb, --nb defines the normal background color. ()\n" " -nf, --nf defines the normal foreground color. ()\n" " -sb, --sb defines the selected background color. ()\n" - " -sf, --sf defines the selected foreground color. ()\n" - - - , out); + " -sf, --sf defines the selected foreground color. ()\n", out); exit((out == stderr ? EXIT_FAILURE : EXIT_SUCCESS)); } -- cgit v1.2.3-70-g09d2 From ce46a04999a31c2e8b0edcbf0f1dc3cd010a3b8b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:39:29 +0300 Subject: Add break for no reason. --- client/client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/client/client.c b/client/client.c index df05d34..7bb3ace 100644 --- a/client/client.c +++ b/client/client.c @@ -99,6 +99,7 @@ static void parseArgs(int *argc, char **argv[]) case 'v': printVersion(*argv[0]); exit(EXIT_SUCCESS); + break; case 'i': client.filterMode = BM_FILTER_MODE_DMENU_CASE_INSENSITIVE; -- cgit v1.2.3-70-g09d2 From 5dda1e46e91801c48716c4c7256e38208172c0bc Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:39:41 +0300 Subject: Print usage on wrong options. --- client/client.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/client.c b/client/client.c index 7bb3ace..f46b223 100644 --- a/client/client.c +++ b/client/client.c @@ -133,6 +133,12 @@ static void parseArgs(int *argc, char **argv[]) case 0x103: case 0x104: break; + + case ':': + case '?': + fputs("\n", stderr); + usage(stderr, *argv[0]); + break; } } -- cgit v1.2.3-70-g09d2 From 77f2c264e77d9aa86f35f722062f2af35609b98c Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 20:42:10 +0300 Subject: Make version function exit itself. --- client/client.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/client.c b/client/client.c index f46b223..1857e61 100644 --- a/client/client.c +++ b/client/client.c @@ -25,11 +25,11 @@ static struct { 0 /* monitor */ }; -static void printVersion(const char *name) +static void version(const char *name) { char *base = strrchr(name, '/'); printf("%s v%s\n", (base ? base + 1 : name), bmVersion()); - puts(" Date: Mon, 14 Apr 2014 20:43:01 +0300 Subject: Support disco parameter. --- client/client.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/client/client.c b/client/client.c index 1857e61..749d833 100644 --- a/client/client.c +++ b/client/client.c @@ -1,9 +1,12 @@ +#define _XOPEN_SOURCE 500 #include #include #include #include -#include +#include +#include #include +#include static struct { bmFilterMode filterMode; @@ -25,6 +28,40 @@ static struct { 0 /* monitor */ }; +static void discoTrap(int sig) +{ + (void)sig; + printf("\e[?25h\n"); + fflush(stdout); + exit(EXIT_FAILURE); +} + +static void disco(void) +{ + struct sigaction action; + memset(&action, 0, sizeof(struct sigaction)); + action.sa_handler = discoTrap; + sigaction(SIGABRT, &action, NULL); + sigaction(SIGSEGV, &action, NULL); + sigaction(SIGTRAP, &action, NULL); + sigaction(SIGINT, &action, NULL); + + unsigned int i, cc, c = 80; + printf("\e[?25l"); + while (1) { + for (i = 1; i < c - 1; ++i) { + printf("\r %*s%s %s %s ", (i > c / 2 ? c - i : i), " ", ((i % 2) ? ""), ((i % 4) ? "DISCO" : " "), ((i %2) ? "\\o>" : ""), ((i % 2) ? "*" : "•")); + for (cc = 2; cc < (i > c / 2 ? c - i : i); ++cc) printf(((i % 2) ? "^" : "'")); + fflush(stdout); + usleep(140 * 1000); + } + } + printf("\e[?25h"); + exit(EXIT_SUCCESS); +} + static void version(const char *name) { char *base = strrchr(name, '/'); @@ -80,6 +117,8 @@ static void parseArgs(int *argc, char **argv[]) { "nf", required_argument, 0, 0x102 }, { "sb", required_argument, 0, 0x103 }, { "sf", required_argument, 0, 0x104 }, + + { "disco", no_argument, 0, 0x105 }, { 0, 0, 0, 0 } }; @@ -133,6 +172,10 @@ static void parseArgs(int *argc, char **argv[]) case 0x104: break; + case 0x105: + disco(); + break; + case ':': case '?': fputs("\n", stderr); -- cgit v1.2.3-70-g09d2 From d36f4039d9e58ac2b6304197fb8c4b1ccae0b2d7 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 20:46:15 +0300 Subject: Identation. --- client/client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/client.c b/client/client.c index 749d833..54906c4 100644 --- a/client/client.c +++ b/client/client.c @@ -118,7 +118,7 @@ static void parseArgs(int *argc, char **argv[]) { "sb", required_argument, 0, 0x103 }, { "sf", required_argument, 0, 0x104 }, - { "disco", no_argument, 0, 0x105 }, + { "disco", no_argument, 0, 0x105 }, { 0, 0, 0, 0 } }; -- cgit v1.2.3-70-g09d2 From 5556779dd2b6ac48ef7e137ca0da3ac9190238bf Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 21:18:50 +0300 Subject: Consistency. --- lib/menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/menu.c b/lib/menu.c index d949430..b7ab021 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -45,7 +45,7 @@ bmMenu* bmMenuNew(bmDrawMode drawMode) status = _bmDrawCursesInit(&menu->renderApi); break; - default:break; + default: break; } if (status == 0) { -- cgit v1.2.3-70-g09d2 From cf27013c1d1790e0d5239aa6fc34999e0f173f30 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Tue, 15 Apr 2014 20:06:51 +0300 Subject: Print the missing function and from what library in case of exception. --- lib/draw/curses.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index f8137b8..afe0a98 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -410,7 +410,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) if (!curses.handle) return 0; -#define bmLoadFunction(x) (curses.x = dlsym(curses.handle, #x)) + char *func = NULL; +#define bmLoadFunction(x) (curses.x = dlsym(curses.handle, (func = #x))) if (!bmLoadFunction(initscr)) goto function_pointer_exception; @@ -471,6 +472,7 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) return 1; function_pointer_exception: + fprintf(stderr, "-!- Could not load function '%s' from '%s'\n", func, DL_PATH[i]); _bmDrawCursesFree(); return 0; } -- cgit v1.2.3-70-g09d2 From f77ae857b181ace139cc702f2a997db6ade86b8c Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Tue, 15 Apr 2014 20:07:47 +0300 Subject: It's better to guard whole function. --- lib/draw/curses.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index afe0a98..0f6fdd2 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -252,6 +252,9 @@ static unsigned int _bmDrawCursesDisplayedCount(const bmMenu *menu) static void _bmDrawCursesEndWin(void) { + if (!curses.stdscr) + return; + freopen(TTY, "w", stdout); if (curses.refresh) @@ -260,12 +263,10 @@ static void _bmDrawCursesEndWin(void) if (curses.endwin) curses.endwin(); - if (curses.stdscr) { - dup2(curses.oldStdin, STDIN_FILENO); - dup2(curses.oldStdout, STDOUT_FILENO); - close(curses.oldStdin); - close(curses.oldStdout); - } + dup2(curses.oldStdin, STDIN_FILENO); + dup2(curses.oldStdout, STDOUT_FILENO); + close(curses.oldStdin); + close(curses.oldStdout); curses.stdscr = NULL; } -- cgit v1.2.3-70-g09d2 From 88590ddcfefdb94f2900282ec8b7a9573b53d37f Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Tue, 15 Apr 2014 20:14:42 +0300 Subject: Don't use the enumerator variable. --- lib/draw/curses.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 0f6fdd2..82fd194 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -403,15 +403,15 @@ static void _bmDrawCursesResizeHandler(int sig) int _bmDrawCursesInit(struct _bmRenderApi *api) { memset(&curses, 0, sizeof(curses)); + const char *lib = NULL, *func = NULL; unsigned int i; for (i = 0; DL_PATH[i] && !curses.handle; ++i) - curses.handle = dlopen(DL_PATH[i], RTLD_LAZY); + curses.handle = dlopen((lib = DL_PATH[i]), RTLD_LAZY); if (!curses.handle) return 0; - char *func = NULL; #define bmLoadFunction(x) (curses.x = dlsym(curses.handle, (func = #x))) if (!bmLoadFunction(initscr)) @@ -473,7 +473,7 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) return 1; function_pointer_exception: - fprintf(stderr, "-!- Could not load function '%s' from '%s'\n", func, DL_PATH[i]); + fprintf(stderr, "-!- Could not load function '%s' from '%s'\n", func, lib); _bmDrawCursesFree(); return 0; } -- cgit v1.2.3-70-g09d2 From 8e415197d248560b359bace7ef8f7ffe0f445099 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Tue, 15 Apr 2014 20:31:10 +0300 Subject: This is not used --- lib/draw/curses.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 82fd194..c4e323a 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -54,7 +54,6 @@ static const char *TTY = "/dev/tty"; #undef attron #undef getmaxx #undef getmaxy -#undef timeout /** * Dynamically loaded curses API. -- cgit v1.2.3-70-g09d2 From 953c61f4ad12d2a711d0c91e24e37cae871dcdc3 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Tue, 15 Apr 2014 20:34:21 +0300 Subject: Support older ncurses. --- lib/draw/curses.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/lib/draw/curses.c b/lib/draw/curses.c index c4e323a..a1f23ef 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -42,9 +42,16 @@ static const char *TTY = "/dev/tty"; }; #endif +/* these are implemented as macros in older curses */ +#ifndef NCURSES_OPAQUE +static int wrap_getmaxx(WINDOW *win) { return getmaxx(win); } +static int wrap_getmaxy(WINDOW *win) { return getmaxy(win); } +#endif + /* ncurses.h likes to define stuff for us. * This unforunately mangles with our struct. */ #undef erase +#undef getch #undef get_wch #undef refresh #undef mvprintw @@ -68,6 +75,7 @@ static struct curses { int (*endwin)(void); int (*refresh)(void); int (*erase)(void); + int (*getch)(void); int (*get_wch)(wint_t *wch); int (*mvprintw)(int x, int y, const char *fmt, ...); int (*move)(int x, int y); @@ -278,7 +286,11 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) if (!curses.stdscr) return BM_KEY_NONE; - curses.get_wch((wint_t*)unicode); + if (curses.get_wch) + curses.get_wch((wint_t*)unicode); + else if (curses.getch) + *unicode = curses.getch(); + switch (*unicode) { #if KEY_RESIZE case KEY_RESIZE: @@ -419,7 +431,7 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) goto function_pointer_exception; if (!bmLoadFunction(refresh)) goto function_pointer_exception; - if (!bmLoadFunction(get_wch)) + if (!bmLoadFunction(get_wch) && !bmLoadFunction(getch)) goto function_pointer_exception; if (!bmLoadFunction(erase)) goto function_pointer_exception; @@ -437,10 +449,6 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) goto function_pointer_exception; if (!bmLoadFunction(use_default_colors)) 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(curs_set)) @@ -454,6 +462,16 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) if (!bmLoadFunction(ESCDELAY)) goto function_pointer_exception; +#ifndef NCURSES_OPAQUE + curses.getmaxx = wrap_getmaxx; + curses.getmaxy = wrap_getmaxy; +#else + if (!bmLoadFunction(getmaxx)) + goto function_pointer_exception; + if (!bmLoadFunction(getmaxy)) + goto function_pointer_exception; +#endif + #undef bmLoadFunction api->displayedCount = _bmDrawCursesDisplayedCount; -- cgit v1.2.3-70-g09d2 From 4ce638eb3a5bdefc0301ab39331ba7b059904406 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Wed, 14 May 2014 20:50:32 +0300 Subject: Length check is fragile and breaking behaviour. --- lib/util.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/util.c b/lib/util.c index 2ace7f7..a47ff8d 100644 --- a/lib/util.c +++ b/lib/util.c @@ -57,12 +57,7 @@ size_t _bmStripToken(char *string, const char *token, size_t *outNext) */ int _bmStrupcmp(const char *hay, const char *needle) { - size_t len, len2; - - if ((len = strlen(hay)) != (len2 = strlen(needle))) - return hay[len] - needle[len2]; - - return _bmStrnupcmp(hay, needle, len); + return _bmStrnupcmp(hay, needle, strlen(hay)); } /** -- cgit v1.2.3-70-g09d2 From 536eee6d0bf8eb99b6566f7f4fd646b3cd76f532 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 21 Aug 2014 01:45:52 +0300 Subject: Skip leading whitespace. --- lib/filter.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/filter.c b/lib/filter.c index ad1b937..04733c5 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -54,9 +54,12 @@ static char* _bmFilterTokenize(bmMenu *menu, char ***outTokv, unsigned int *outT if (!(buffer = _bmStrdup(menu->filter))) goto fail; + char *s; + for (s = buffer; *s && *s == ' '; ++s); + + char **tmp = NULL; size_t pos = 0, next; unsigned int tokc = 0, tokn = 0; - char *s = buffer, **tmp = NULL; for (; (pos = _bmStripToken(s, " ", &next)) > 0; tokv = tmp) { if (++tokc > tokn && !(tmp = realloc(tokv, ++tokn * sizeof(char*)))) goto fail; -- cgit v1.2.3-70-g09d2