forked from RepoMirrors/bemenu
Implement list structure, and feature for multiple selections.
This commit is contained in:
parent
16e023aa3e
commit
a6d0413b97
@ -101,10 +101,10 @@ int main(int argc, char **argv)
|
|||||||
} while ((status = bmMenuRunWithKey(menu, key, unicode)) == BM_RUN_RESULT_RUNNING);
|
} while ((status = bmMenuRunWithKey(menu, key, unicode)) == BM_RUN_RESULT_RUNNING);
|
||||||
|
|
||||||
if (status == BM_RUN_RESULT_SELECTED) {
|
if (status == BM_RUN_RESULT_SELECTED) {
|
||||||
bmItem *item = bmMenuGetSelectedItem(menu);
|
unsigned int i, count;
|
||||||
|
bmItem **items = bmMenuGetSelectedItems(menu, &count);
|
||||||
if (item)
|
for (i = 0; i < count; ++i)
|
||||||
printf("%s\n", bmItemGetText(item));
|
printf("%s\n", bmItemGetText(items[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
bmMenuFree(menu);
|
bmMenuFree(menu);
|
||||||
|
@ -3,6 +3,7 @@ SET(BEMENU_SOURCE
|
|||||||
menu.c
|
menu.c
|
||||||
item.c
|
item.c
|
||||||
filter.c
|
filter.c
|
||||||
|
list.c
|
||||||
util.c
|
util.c
|
||||||
draw/curses.c
|
draw/curses.c
|
||||||
)
|
)
|
||||||
|
62
lib/bemenu.h
62
lib/bemenu.h
@ -85,6 +85,7 @@ typedef enum bmKey {
|
|||||||
BM_KEY_TAB,
|
BM_KEY_TAB,
|
||||||
BM_KEY_ESCAPE,
|
BM_KEY_ESCAPE,
|
||||||
BM_KEY_RETURN,
|
BM_KEY_RETURN,
|
||||||
|
BM_KEY_SHIFT_RETURN,
|
||||||
BM_KEY_UNICODE,
|
BM_KEY_UNICODE,
|
||||||
BM_KEY_LAST
|
BM_KEY_LAST
|
||||||
} bmKey;
|
} bmKey;
|
||||||
@ -200,24 +201,52 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index);
|
|||||||
*/
|
*/
|
||||||
int bmMenuRemoveItem(bmMenu *menu, bmItem *item);
|
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.
|
* Get highlighted item 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 highlighted item.
|
||||||
|
* @return Selected bmItem instance, **NULL** if none highlighted.
|
||||||
|
*/
|
||||||
|
bmItem* bmMenuGetHighlightedItem(const bmMenu *menu);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight item in menu by index.
|
||||||
*
|
*
|
||||||
* @param menu bmMenu instance from where to get 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 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.
|
* @param outNmemb Reference to unsigned int where total count of returned items will be stored.
|
||||||
* @return Pointer to array of bmItem pointers.
|
* @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.
|
* 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);
|
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.
|
* Set items to bmMenu instance.
|
||||||
* Will replace all the old items on bmMenu instance.
|
* Will replace all the old items on bmMenu instance.
|
||||||
|
@ -120,8 +120,9 @@ static void _bmDrawCursesRender(const bmMenu *menu)
|
|||||||
unsigned int itemsCount;
|
unsigned int itemsCount;
|
||||||
bmItem **items = bmMenuGetFilteredItems(menu, &itemsCount);
|
bmItem **items = bmMenuGetFilteredItems(menu, &itemsCount);
|
||||||
for (i = (menu->index / (lines - 1)) * (lines - 1); i < itemsCount && cl < lines; ++i) {
|
for (i = (menu->index / (lines - 1)) * (lines - 1); i < itemsCount && cl < lines; ++i) {
|
||||||
int selected = (items[i] == bmMenuGetSelectedItem(menu));
|
int highlighted = (items[i] == bmMenuGetHighlightedItem(menu));
|
||||||
_bmDrawCursesDrawLine((selected ? 2 : 0), cl++, "%s%s", (selected ? ">> " : " "), items[i]->text);
|
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);
|
curses.move(0, titleLen + menu->cursesCursor);
|
||||||
@ -200,6 +201,9 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode)
|
|||||||
case 9: /* Tab */
|
case 9: /* Tab */
|
||||||
return BM_KEY_TAB;
|
return BM_KEY_TAB;
|
||||||
|
|
||||||
|
case 18: /* C-r */
|
||||||
|
return BM_KEY_SHIFT_RETURN;
|
||||||
|
|
||||||
case 10: /* Return */
|
case 10: /* Return */
|
||||||
_bmDrawCursesEndWin();
|
_bmDrawCursesEndWin();
|
||||||
return BM_KEY_RETURN;
|
return BM_KEY_RETURN;
|
||||||
|
28
lib/filter.c
28
lib/filter.c
@ -8,33 +8,33 @@
|
|||||||
*
|
*
|
||||||
* @param menu bmMenu instance to filter.
|
* @param menu bmMenu instance to filter.
|
||||||
* @param outNmemb unsigned int reference to filtered items outNmemb.
|
* @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.
|
* @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(menu);
|
||||||
assert(outNmemb);
|
assert(outNmemb);
|
||||||
assert(outSelected);
|
assert(outHighlighted);
|
||||||
*outNmemb = *outSelected = 0;
|
*outNmemb = *outHighlighted = 0;
|
||||||
|
|
||||||
/* FIXME: not real dmenu like filtering at all */
|
/* 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)
|
if (!filtered)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
unsigned int i, f;
|
unsigned int i, f;
|
||||||
for (f = i = 0; i < menu->itemsCount; ++i) {
|
for (f = i = 0; i < menu->items.count; ++i) {
|
||||||
bmItem *item = menu->items[i];
|
bmItem *item = menu->items.list[i];
|
||||||
if (item->text && strstr(item->text, menu->filter)) {
|
if (item->text && strstr(item->text, menu->filter)) {
|
||||||
if (f == 0 || item == bmMenuGetSelectedItem(menu))
|
if (f == 0 || item == bmMenuGetHighlightedItem(menu))
|
||||||
*outSelected = f;
|
*outHighlighted = f;
|
||||||
filtered[f++] = item;
|
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 menu bmMenu instance to filter.
|
||||||
* @param outNmemb unsigned int reference to filtered items outNmemb.
|
* @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.
|
* @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(menu);
|
||||||
assert(outNmemb);
|
assert(outNmemb);
|
||||||
assert(outSelected);
|
assert(outHighlighted);
|
||||||
*outNmemb = *outSelected = 0;
|
*outNmemb = *outHighlighted = 0;
|
||||||
|
|
||||||
/* FIXME: stub */
|
/* FIXME: stub */
|
||||||
|
|
||||||
|
@ -38,6 +38,23 @@ struct _bmRenderApi {
|
|||||||
void (*free)(void);
|
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.
|
* Internal bmMenu struct that is not exposed to public.
|
||||||
*/
|
*/
|
||||||
@ -48,14 +65,19 @@ struct _bmMenu {
|
|||||||
struct _bmRenderApi renderApi;
|
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.
|
* Filtered/displayed items contained in menu instance.
|
||||||
*/
|
*/
|
||||||
struct _bmItem **filteredItems;
|
struct _bmItemList filtered;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selected items.
|
||||||
|
*/
|
||||||
|
struct _bmItemList selection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Menu instance title.
|
* Menu instance title.
|
||||||
@ -79,22 +101,7 @@ struct _bmMenu {
|
|||||||
unsigned int cursesCursor;
|
unsigned int cursesCursor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of items in menu instance.
|
* Current filtered/highlighted item index 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.
|
* This index is valid for the list returned by bmMenuGetFilteredItems.
|
||||||
*/
|
*/
|
||||||
unsigned int index;
|
unsigned int index;
|
||||||
@ -113,9 +120,24 @@ struct _bmMenu {
|
|||||||
/* draw/curses.c */
|
/* draw/curses.c */
|
||||||
int _bmDrawCursesInit(struct _bmRenderApi *api);
|
int _bmDrawCursesInit(struct _bmRenderApi *api);
|
||||||
|
|
||||||
|
/* menu.c */
|
||||||
|
int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item);
|
||||||
|
|
||||||
/* filter.c */
|
/* filter.c */
|
||||||
bmItem** _bmFilterDmenu(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 *outSelected);
|
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 */
|
/* util.c */
|
||||||
char* _bmStrdup(const char *s);
|
char* _bmStrdup(const char *s);
|
||||||
|
135
lib/list.c
Normal file
135
lib/list.c
Normal file
@ -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 :*/
|
227
lib/menu.c
227
lib/menu.c
@ -7,7 +7,7 @@
|
|||||||
/**
|
/**
|
||||||
* Filter function map.
|
* 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 */
|
_bmFilterDmenu, /* BM_FILTER_DMENU */
|
||||||
_bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */
|
_bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */
|
||||||
};
|
};
|
||||||
@ -16,37 +16,22 @@ static void _bmMenuFilter(bmMenu *menu)
|
|||||||
{
|
{
|
||||||
assert(menu);
|
assert(menu);
|
||||||
|
|
||||||
if (menu->filteredItems)
|
|
||||||
free(menu->filteredItems);
|
|
||||||
|
|
||||||
menu->filteredCount = 0;
|
|
||||||
menu->filteredItems = NULL;
|
|
||||||
|
|
||||||
unsigned int count, selected;
|
unsigned int count, selected;
|
||||||
bmItem **filtered = filterFunc[menu->filterMode](menu, &count, &selected);
|
bmItem **filtered = filterFunc[menu->filterMode](menu, &count, &selected);
|
||||||
|
|
||||||
menu->filteredItems = filtered;
|
_bmItemListSetItemsNoCopy(&menu->filtered, filtered, count);
|
||||||
menu->filteredCount = count;
|
|
||||||
menu->index = selected;
|
menu->index = selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _bmMenuGrowItems(bmMenu *menu)
|
int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item)
|
||||||
{
|
{
|
||||||
void *tmp;
|
assert(menu);
|
||||||
static const unsigned int step = 32;
|
assert(item);
|
||||||
unsigned int nsize = sizeof(bmItem*) * (menu->allocatedCount + step);
|
|
||||||
|
|
||||||
if (!(tmp = realloc(menu->items, nsize))) {
|
unsigned int i, count;
|
||||||
if (!(tmp = malloc(nsize)))
|
bmItem **items = bmMenuGetSelectedItems(menu, &count);
|
||||||
return 0;
|
for (i = 0; i < count && items[i] != item; ++i);
|
||||||
|
return (i < count);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,8 +82,8 @@ void bmMenuFree(bmMenu *menu)
|
|||||||
if (menu->title)
|
if (menu->title)
|
||||||
free(menu->title);
|
free(menu->title);
|
||||||
|
|
||||||
if (menu->filteredItems)
|
if (menu->filtered.list)
|
||||||
free(menu->filteredItems);
|
free(menu->filtered.list);
|
||||||
|
|
||||||
bmMenuFreeItems(menu);
|
bmMenuFreeItems(menu);
|
||||||
free(menu);
|
free(menu);
|
||||||
@ -112,14 +97,9 @@ void bmMenuFree(bmMenu *menu)
|
|||||||
void bmMenuFreeItems(bmMenu *menu)
|
void bmMenuFreeItems(bmMenu *menu)
|
||||||
{
|
{
|
||||||
assert(menu);
|
assert(menu);
|
||||||
|
_bmItemListFreeList(&menu->selection);
|
||||||
unsigned int i;
|
_bmItemListFreeList(&menu->filtered);
|
||||||
for (i = 0; i < menu->itemsCount; ++i)
|
_bmItemListFreeItems(&menu->items);
|
||||||
bmItemFree(menu->items[i]);
|
|
||||||
|
|
||||||
free(menu->items);
|
|
||||||
menu->allocatedCount = menu->itemsCount = 0;
|
|
||||||
menu->items = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -195,19 +175,7 @@ const char* bmMenuGetTitle(const bmMenu *menu)
|
|||||||
int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index)
|
int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index)
|
||||||
{
|
{
|
||||||
assert(menu);
|
assert(menu);
|
||||||
assert(item);
|
return _bmItemListAddItemAt(&menu->items, item, index);;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -219,7 +187,7 @@ int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index)
|
|||||||
*/
|
*/
|
||||||
int bmMenuAddItem(bmMenu *menu, bmItem *item)
|
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);
|
assert(menu);
|
||||||
|
|
||||||
unsigned int i = index;
|
if (!menu->items.list || menu->items.count <= index)
|
||||||
if (i >= menu->itemsCount)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
memmove(&menu->items[i], &menu->items[i], sizeof(bmItem*) * (menu->itemsCount - i));
|
bmItem *item = menu->items.list[index];
|
||||||
return 1;
|
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)
|
int bmMenuRemoveItem(bmMenu *menu, bmItem *item)
|
||||||
{
|
{
|
||||||
assert(menu);
|
assert(menu);
|
||||||
assert(item);
|
|
||||||
|
|
||||||
unsigned int i;
|
int ret = _bmItemListRemoveItem(&menu->items, item);
|
||||||
for (i = 0; i < menu->itemsCount && menu->items[i] != item; ++i);
|
|
||||||
return bmMenuRemoveItemAt(menu, i);
|
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.
|
* @param menu bmMenu instance from where to get highlighted item.
|
||||||
* @return Selected bmItem instance, **NULL** if none selected.
|
* @return Selected bmItem instance, **NULL** if none highlighted.
|
||||||
*/
|
*/
|
||||||
bmItem* bmMenuGetSelectedItem(const bmMenu *menu)
|
bmItem* bmMenuGetHighlightedItem(const bmMenu *menu)
|
||||||
{
|
{
|
||||||
assert(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.
|
||||||
* @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)
|
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 highlight item.
|
||||||
|
* @param item bmItem instance to highlight.
|
||||||
|
* @return 1 on successful highlight, 0 on failure.
|
||||||
|
*/
|
||||||
|
int bmMenuSetHighlighted(bmMenu *menu, bmItem *item)
|
||||||
{
|
{
|
||||||
assert(menu);
|
assert(menu);
|
||||||
|
|
||||||
if (outNmemb)
|
unsigned int i, itemsCount;
|
||||||
*outNmemb = menu->itemsCount;
|
bmItem **items = bmMenuGetFilteredItems(menu, &itemsCount);
|
||||||
|
for (i = 0; i < itemsCount && items[i] != item; ++i);
|
||||||
|
|
||||||
return menu->items;
|
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** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb)
|
||||||
|
{
|
||||||
|
assert(menu);
|
||||||
|
return _bmItemListGetItems(&menu->selection, 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)
|
||||||
|
{
|
||||||
|
assert(menu);
|
||||||
|
return _bmItemListSetItemsNoCopy(&menu->selection, items, nmemb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -314,10 +340,25 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb)
|
|||||||
{
|
{
|
||||||
assert(menu);
|
assert(menu);
|
||||||
|
|
||||||
if (outNmemb)
|
if (menu->filtered.list)
|
||||||
*outNmemb = (menu->filteredItems ? menu->filteredCount : menu->itemsCount);
|
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);
|
assert(menu);
|
||||||
|
|
||||||
if (!items || nmemb == 0) {
|
int ret = _bmItemListSetItems(&menu->items, items, nmemb);
|
||||||
bmMenuFreeItems(menu);
|
|
||||||
return 1;
|
if (ret) {
|
||||||
|
_bmItemListFreeList(&menu->selection);
|
||||||
|
_bmItemListFreeList(&menu->filtered);
|
||||||
}
|
}
|
||||||
|
|
||||||
bmItem **newItems;
|
return ret;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -400,7 +434,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode)
|
|||||||
{
|
{
|
||||||
assert(menu);
|
assert(menu);
|
||||||
char *oldFilter = _bmStrdup(menu->filter);
|
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) {
|
switch (key) {
|
||||||
case BM_KEY_LEFT:
|
case BM_KEY_LEFT:
|
||||||
@ -502,9 +536,9 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode)
|
|||||||
|
|
||||||
case BM_KEY_TAB:
|
case BM_KEY_TAB:
|
||||||
{
|
{
|
||||||
bmItem *selected = bmMenuGetSelectedItem(menu);
|
bmItem *highlighted = bmMenuGetHighlightedItem(menu);
|
||||||
if (selected && bmItemGetText(selected)) {
|
if (highlighted && bmItemGetText(highlighted)) {
|
||||||
const char *text = bmItemGetText(selected);
|
const char *text = bmItemGetText(highlighted);
|
||||||
size_t len = strlen(text);
|
size_t len = strlen(text);
|
||||||
|
|
||||||
if (len > sizeof(menu->filter) - 1)
|
if (len > sizeof(menu->filter) - 1)
|
||||||
@ -518,6 +552,19 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode)
|
|||||||
}
|
}
|
||||||
break;
|
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;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user