forked from RepoMirrors/bemenu
Proper filtering functions.
This commit is contained in:
parent
05212f5e44
commit
45502a2fd9
155
lib/filter.c
155
lib/filter.c
@ -3,6 +3,129 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* 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** _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;
|
||||
|
||||
unsigned int itemsCount;
|
||||
bmItem **items = bmMenuGetItems(menu, &itemsCount);
|
||||
|
||||
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 < 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;
|
||||
}
|
||||
|
||||
if (buffer)
|
||||
free(buffer);
|
||||
|
||||
if (tokv)
|
||||
free(tokv);
|
||||
|
||||
return _bmFilterShrinkList(&filtered, menu->items.count, (*outNmemb = f));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter that mimics the vanilla dmenu filtering.
|
||||
*
|
||||
@ -13,28 +136,7 @@
|
||||
*/
|
||||
bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted)
|
||||
{
|
||||
assert(menu);
|
||||
assert(outNmemb);
|
||||
assert(outHighlighted);
|
||||
*outNmemb = *outHighlighted = 0;
|
||||
|
||||
/* FIXME: not real dmenu like filtering at all */
|
||||
|
||||
bmItem **filtered = calloc(menu->items.count, sizeof(bmItem*));
|
||||
if (!filtered)
|
||||
return NULL;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return _bmShrinkItemList(&filtered, menu->items.count, (*outNmemb = f));
|
||||
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 :*/
|
||||
|
@ -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);
|
||||
|
37
lib/menu.c
37
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:
|
||||
|
62
lib/util.c
62
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 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 match against.
|
||||
* @param needle C "string" to match.
|
||||
*/
|
||||
bmItem** _bmShrinkItemList(bmItem ***inOutList, size_t osize, size_t nsize)
|
||||
int _bmStrupcmp(const char *hay, const char *needle)
|
||||
{
|
||||
assert(inOutList);
|
||||
size_t i, len;
|
||||
|
||||
if (nsize >= osize)
|
||||
return *inOutList;
|
||||
if ((len = strlen(hay)) != strlen(needle))
|
||||
return 1;
|
||||
|
||||
void *tmp = malloc(sizeof(bmItem*) * nsize);
|
||||
if (!tmp)
|
||||
return *inOutList;
|
||||
for (i = 0; i != len; ++i)
|
||||
if (toupper(hay[i]) != toupper(needle[i]))
|
||||
return 1;
|
||||
|
||||
memcpy(tmp, *inOutList, sizeof(bmItem*) * nsize);
|
||||
free(*inOutList);
|
||||
*inOutList = tmp;
|
||||
return *inOutList;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Portable case-insensitive strstr.
|
||||
*
|
||||
* @param hay C "string" to substring against.
|
||||
* @param needle C "string" to substring.
|
||||
*/
|
||||
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 (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;
|
||||
}
|
||||
}
|
||||
|
||||
return (p == len2 ? (char*)hay + r : NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user