Added support for fuzzy searching
This commit is contained in:
parent
c8d052be6a
commit
50838d3d98
@ -21,6 +21,7 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -32,4 +33,10 @@ void utils_sleep_millis(time_t millis);
|
|||||||
|
|
||||||
char* utils_concat(size_t arg_count, ...);
|
char* utils_concat(size_t arg_count, ...);
|
||||||
|
|
||||||
|
size_t utils_min(size_t n1, size_t n2);
|
||||||
|
|
||||||
|
size_t utils_min3(size_t n1, size_t n2, size_t n3);
|
||||||
|
|
||||||
|
size_t utils_distance(const char* str1, const char* str2);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
16
src/main.c
16
src/main.c
@ -68,6 +68,7 @@ static void print_usage(char** argv) {
|
|||||||
printf("--password\t-P\tRuns in password mode\n");
|
printf("--password\t-P\tRuns in password mode\n");
|
||||||
printf("--exec-search\t-e\tMakes enter always use the search contents not the first result\n");
|
printf("--exec-search\t-e\tMakes enter always use the search contents not the first result\n");
|
||||||
printf("--hide-scroll\t-b\tHides the scroll bars\n");
|
printf("--hide-scroll\t-b\tHides the scroll bars\n");
|
||||||
|
printf("--matching\t-M\tSets the matching method, default is contains\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,6 +298,12 @@ int main(int argc, char** argv) {
|
|||||||
.flag = NULL,
|
.flag = NULL,
|
||||||
.val = 'b'
|
.val = 'b'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "matching",
|
||||||
|
.has_arg = required_argument,
|
||||||
|
.flag = NULL,
|
||||||
|
.val = 'M'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.name = NULL,
|
.name = NULL,
|
||||||
.has_arg = 0,
|
.has_arg = 0,
|
||||||
@ -322,8 +329,9 @@ int main(int argc, char** argv) {
|
|||||||
char* password_char = "false";
|
char* password_char = "false";
|
||||||
char* exec_search = NULL;
|
char* exec_search = NULL;
|
||||||
char* hide_scroll = NULL;
|
char* hide_scroll = NULL;
|
||||||
|
char* matching = NULL;
|
||||||
int opt;
|
int opt;
|
||||||
while((opt = getopt_long(argc, argv, "hfc:s:C:dS:W:H:p:x:y:nimk:t:P::eb", opts, NULL)) != -1) {
|
while((opt = getopt_long(argc, argv, "hfc:s:C:dS:W:H:p:x:y:nimk:t:P::ebM:", opts, NULL)) != -1) {
|
||||||
switch(opt) {
|
switch(opt) {
|
||||||
case 'h':
|
case 'h':
|
||||||
print_usage(argv);
|
print_usage(argv);
|
||||||
@ -390,6 +398,9 @@ int main(int argc, char** argv) {
|
|||||||
case 'b':
|
case 'b':
|
||||||
hide_scroll = "true";
|
hide_scroll = "true";
|
||||||
break;
|
break;
|
||||||
|
case 'M':
|
||||||
|
matching = optarg;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,6 +537,9 @@ int main(int argc, char** argv) {
|
|||||||
if(hide_scroll != NULL) {
|
if(hide_scroll != NULL) {
|
||||||
map_put(config, "hide_scroll", hide_scroll);
|
map_put(config, "hide_scroll", hide_scroll);
|
||||||
}
|
}
|
||||||
|
if(matching != NULL) {
|
||||||
|
map_put(config, "matching", matching);
|
||||||
|
}
|
||||||
|
|
||||||
gtk_init(&argc, &argv);
|
gtk_init(&argc, &argv);
|
||||||
|
|
||||||
|
46
src/utils.c
46
src/utils.c
@ -47,3 +47,49 @@ char* utils_concat(size_t arg_count, ...) {
|
|||||||
va_end(args);
|
va_end(args);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t utils_min(size_t n1, size_t n2) {
|
||||||
|
if(n1 < n2) {
|
||||||
|
return n1;
|
||||||
|
} else {
|
||||||
|
return n2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t utils_min3(size_t n1, size_t n2, size_t n3) {
|
||||||
|
if(n1 < n2 && n1 < n3) {
|
||||||
|
return n1;
|
||||||
|
} else if(n2 < n1 && n2 < n3) {
|
||||||
|
return n2;
|
||||||
|
} else {
|
||||||
|
return n3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t utils_distance(const char* str1, const char* str2) {
|
||||||
|
size_t str1_len = strlen(str1);
|
||||||
|
size_t str2_len = strlen(str2);
|
||||||
|
|
||||||
|
size_t arr[str1_len + 1][str2_len + 1];
|
||||||
|
arr[0][0] = 0;
|
||||||
|
for(size_t count = 1; count <= str1_len; ++count) {
|
||||||
|
arr[count][0] = count;
|
||||||
|
}
|
||||||
|
for(size_t count = 1; count <= str2_len; ++count) {
|
||||||
|
arr[0][count] = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t cost;
|
||||||
|
for(size_t c1 = 1; c1 <= str1_len; ++c1) {
|
||||||
|
for(size_t c2 = 1; c2 <= str2_len; ++c2) {
|
||||||
|
if(str1[c1 - 1] == str2[c2 - 1]) {
|
||||||
|
cost = 0;
|
||||||
|
} else {
|
||||||
|
cost = 1;
|
||||||
|
}
|
||||||
|
arr[c1][c2] = utils_min3(arr[c1 - 1][c2] + 1, arr[c1][c2 - 1] + 1, arr[c1 - 1][c2 - 1] + cost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr[str1_len][str2_len];
|
||||||
|
}
|
||||||
|
51
src/wofi.c
51
src/wofi.c
@ -19,6 +19,11 @@
|
|||||||
|
|
||||||
static const char* terminals[] = {"kitty", "termite", "gnome-terminal", "weston-terminal"};
|
static const char* terminals[] = {"kitty", "termite", "gnome-terminal", "weston-terminal"};
|
||||||
|
|
||||||
|
enum matching_mode {
|
||||||
|
MATCHING_MODE_CONTAINS,
|
||||||
|
MATCHING_MODE_FUZZY
|
||||||
|
};
|
||||||
|
|
||||||
static uint64_t width, height;
|
static uint64_t width, height;
|
||||||
static int64_t x, y;
|
static int64_t x, y;
|
||||||
static struct zwlr_layer_shell_v1* shell;
|
static struct zwlr_layer_shell_v1* shell;
|
||||||
@ -37,6 +42,7 @@ static char* terminal;
|
|||||||
static GtkOrientation outer_orientation;
|
static GtkOrientation outer_orientation;
|
||||||
static bool exec_search;
|
static bool exec_search;
|
||||||
static struct map* modes;
|
static struct map* modes;
|
||||||
|
static enum matching_mode matching;
|
||||||
|
|
||||||
struct node {
|
struct node {
|
||||||
size_t action_count;
|
size_t action_count;
|
||||||
@ -75,6 +81,7 @@ static void get_input(GtkSearchEntry* entry, gpointer data) {
|
|||||||
filter = gtk_entry_get_text(GTK_ENTRY(entry));
|
filter = gtk_entry_get_text(GTK_ENTRY(entry));
|
||||||
filter_time = utils_get_time_millis();
|
filter_time = utils_get_time_millis();
|
||||||
gtk_flow_box_invalidate_filter(GTK_FLOW_BOX(inner_box));
|
gtk_flow_box_invalidate_filter(GTK_FLOW_BOX(inner_box));
|
||||||
|
gtk_flow_box_invalidate_sort(GTK_FLOW_BOX(inner_box));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,6 +89,7 @@ static void get_search(GtkSearchEntry* entry, gpointer data) {
|
|||||||
(void) data;
|
(void) data;
|
||||||
filter = gtk_entry_get_text(GTK_ENTRY(entry));
|
filter = gtk_entry_get_text(GTK_ENTRY(entry));
|
||||||
gtk_flow_box_invalidate_filter(GTK_FLOW_BOX(inner_box));
|
gtk_flow_box_invalidate_filter(GTK_FLOW_BOX(inner_box));
|
||||||
|
gtk_flow_box_invalidate_sort(GTK_FLOW_BOX(inner_box));
|
||||||
}
|
}
|
||||||
|
|
||||||
static GtkWidget* create_label(char* mode, char* text, char* search_text, char* action) {
|
static GtkWidget* create_label(char* mode, char* text, char* search_text, char* action) {
|
||||||
@ -435,10 +443,37 @@ static gboolean do_filter(GtkFlowBoxChild* row, gpointer data) {
|
|||||||
if(text == NULL) {
|
if(text == NULL) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
if(strcasestr(text, filter) != NULL) {
|
return strstr(text, filter) != NULL;
|
||||||
return TRUE;
|
}
|
||||||
|
|
||||||
|
static gint do_sort(GtkFlowBoxChild* child1, GtkFlowBoxChild* child2, gpointer data) {
|
||||||
|
(void) data;
|
||||||
|
GtkWidget* box1 = gtk_bin_get_child(GTK_BIN(child1));
|
||||||
|
GtkWidget* box2 = gtk_bin_get_child(GTK_BIN(child2));
|
||||||
|
if(GTK_IS_EXPANDER(box1)) {
|
||||||
|
box1 = gtk_expander_get_label_widget(GTK_EXPANDER(box1));
|
||||||
|
}
|
||||||
|
if(GTK_IS_EXPANDER(box2)) {
|
||||||
|
box2 = gtk_expander_get_label_widget(GTK_EXPANDER(box2));
|
||||||
|
}
|
||||||
|
|
||||||
|
const gchar* text1 = wofi_property_box_get_property(WOFI_PROPERTY_BOX(box1), "filter");
|
||||||
|
const gchar* text2 = wofi_property_box_get_property(WOFI_PROPERTY_BOX(box2), "filter");
|
||||||
|
if(filter == NULL || strcmp(filter, "") == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(text1 == NULL || text2 == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t dist1 = utils_distance(text1, filter);
|
||||||
|
size_t dist2 = utils_distance(text2, filter);
|
||||||
|
if(dist1 < dist2) {
|
||||||
|
return -1;
|
||||||
|
} else if(dist1 > dist2) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean key_press(GtkWidget* widget, GdkEvent* event, gpointer data) {
|
static gboolean key_press(GtkWidget* widget, GdkEvent* event, gpointer data) {
|
||||||
@ -566,6 +601,7 @@ void wofi_init(struct map* config) {
|
|||||||
char* password_char = map_get(config, "password_char");
|
char* password_char = map_get(config, "password_char");
|
||||||
exec_search = strcmp(config_get(config, "exec_search", "false"), "true") == 0;
|
exec_search = strcmp(config_get(config, "exec_search", "false"), "true") == 0;
|
||||||
bool hide_scroll = strcmp(config_get(config, "hide_scroll", "false"), "true") == 0;
|
bool hide_scroll = strcmp(config_get(config, "hide_scroll", "false"), "true") == 0;
|
||||||
|
matching = config_get_mnemonic(config, "matching", "contains", 2, "contains", "fuzzy");
|
||||||
modes = map_init_void();
|
modes = map_init_void();
|
||||||
|
|
||||||
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||||
@ -631,7 +667,14 @@ void wofi_init(struct map* config) {
|
|||||||
gtk_container_add(GTK_CONTAINER(wrapper_box), inner_box);
|
gtk_container_add(GTK_CONTAINER(wrapper_box), inner_box);
|
||||||
gtk_container_add(GTK_CONTAINER(scroll), wrapper_box);
|
gtk_container_add(GTK_CONTAINER(scroll), wrapper_box);
|
||||||
|
|
||||||
gtk_flow_box_set_filter_func(GTK_FLOW_BOX(inner_box), do_filter, NULL, NULL);
|
switch(matching) {
|
||||||
|
case MATCHING_MODE_CONTAINS:
|
||||||
|
gtk_flow_box_set_filter_func(GTK_FLOW_BOX(inner_box), do_filter, NULL, NULL);
|
||||||
|
break;
|
||||||
|
case MATCHING_MODE_FUZZY:
|
||||||
|
gtk_flow_box_set_sort_func(GTK_FLOW_BOX(inner_box), do_sort, NULL, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
g_signal_connect(entry, "changed", G_CALLBACK(get_input), NULL);
|
g_signal_connect(entry, "changed", G_CALLBACK(get_input), NULL);
|
||||||
g_signal_connect(entry, "search-changed", G_CALLBACK(get_search), NULL);
|
g_signal_connect(entry, "search-changed", G_CALLBACK(get_search), NULL);
|
||||||
|
Loading…
Reference in New Issue
Block a user