diff options
| -rw-r--r-- | client/client.c | 8 | ||||
| -rw-r--r-- | lib/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | lib/bemenu.h | 56 | ||||
| -rw-r--r-- | lib/draw/curses.c | 8 | ||||
| -rw-r--r-- | lib/filter.c | 28 | ||||
| -rw-r--r-- | lib/internal.h | 64 | ||||
| -rw-r--r-- | lib/list.c | 135 | ||||
| -rw-r--r-- | lib/menu.c | 223 | 
8 files changed, 386 insertions, 137 deletions
| 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. @@ -232,6 +261,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 <stdlib.h> +#include <string.h> +#include <assert.h> + +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 :*/ @@ -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;      } | 
