Proper filtering functions.

This commit is contained in:
Jari Vetoniemi 2014-04-10 22:02:47 +03:00
parent 05212f5e44
commit 45502a2fd9
4 changed files with 200 additions and 56 deletions

View File

@ -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 :*/

View File

@ -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);

View File

@ -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:

View File

@ -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);
}
/**