From 8f362e787b90f6de41622d72818ade5063507023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Sun, 21 Oct 2018 00:38:18 +0200 Subject: [PATCH] Add a search feature for Library tab --- nncmpp.c | 140 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 113 insertions(+), 27 deletions(-) diff --git a/nncmpp.c b/nncmpp.c index b67453f..dfde884 100644 --- a/nncmpp.c +++ b/nncmpp.c @@ -1545,6 +1545,7 @@ app_goto_tab (int tab_index) XX( MPD_VOLUME_UP, "Increase volume" ) \ XX( MPD_VOLUME_DOWN, "Decrease volume" ) \ \ + XX( MPD_SEARCH, "Global search" ) \ XX( MPD_ADD, "Add selection to playlist" ) \ XX( MPD_REPLACE, "Replace playlist" ) \ XX( MPD_UPDATE_DB, "Update MPD database" ) \ @@ -2001,6 +2002,7 @@ g_normal_defaults[] = { "Delete", ACTION_DELETE }, { "Backspace", ACTION_UP }, { "v", ACTION_MULTISELECT }, + { "/", ACTION_MPD_SEARCH }, { "a", ACTION_MPD_ADD }, { "r", ACTION_MPD_REPLACE }, { ":", ACTION_MPD_COMMAND }, @@ -2268,6 +2270,8 @@ static struct struct str path; ///< Current path struct strv items; ///< Current items (type, name, path) struct library_level *above; ///< Upper levels + + bool searching; ///< Search mode is active } g_library_tab; @@ -2375,13 +2379,13 @@ library_tab_parent (void) } static bool -library_tab_is_above (const char *above, const char *path) +library_tab_is_above (const char *above, const char *subdir) { size_t above_len = strlen (above); - if (strncmp (above, path, above_len)) + if (strncmp (above, subdir, above_len)) return false; // The root is an empty string and is above anything other than itself - return path[above_len] == '/' || (*path && !*above); + return subdir[above_len] == '/' || (*subdir && !*above); } static void @@ -2427,20 +2431,9 @@ library_tab_change_level (const char *new_path) } static void -library_tab_on_data (const struct mpd_response *response, - const struct strv *data, void *user_data) +library_tab_load_data (const struct strv *data) { - char *new_path = user_data; - if (!response->success) - { - print_error ("cannot read directory: %s", response->message_text); - free (new_path); - return; - } - strv_reset (&g_library_tab.items); - library_tab_change_level (new_path); - free (new_path); char *parent = library_tab_parent (); if (parent) @@ -2481,9 +2474,31 @@ library_tab_on_data (const struct mpd_response *response, app_invalidate (); } +static void +library_tab_on_data (const struct mpd_response *response, + const struct strv *data, void *user_data) +{ + char *new_path = user_data; + if (!response->success) + { + print_error ("cannot read directory: %s", response->message_text); + free (new_path); + return; + } + + g_library_tab.searching = false; + library_tab_change_level (new_path); + free (new_path); + + library_tab_load_data (data); +} + static void library_tab_reload (const char *new_path) { + if (!new_path && g_library_tab.searching) + return; // TODO: perhaps we should call search_on_changed() + char *path = new_path ? xstrdup (new_path) : xstrdup (g_library_tab.path.str); @@ -2494,6 +2509,48 @@ library_tab_reload (const char *new_path) mpd_client_idle (c, 0); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +static void +library_tab_on_search_data (const struct mpd_response *response, + const struct strv *data, void *user_data) +{ + (void) user_data; + if (!g_library_tab.searching) + return; + + if (!response->success) + { + print_error ("cannot search: %s", response->message_text); + return; + } + + library_tab_load_data (data); +} + +static void +search_on_changed (void) +{ + struct mpd_client *c = &g.client; + + size_t len; + char *u8 = (char *) u32_to_u8 (g.editor.line, g.editor.len + 1, NULL, &len); + mpd_client_send_command (c, "search", "any", u8, NULL); + free (u8); + + mpd_client_add_task (c, library_tab_on_search_data, NULL); + mpd_client_idle (c, 0); +} + +static void +search_on_end (bool confirmed) +{ + if (!confirmed) + library_tab_reload (g_library_tab.above->path); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + static bool library_tab_is_range_playable (struct tab_range range) { @@ -2544,6 +2601,47 @@ library_tab_on_action (enum action action) } return parent != NULL; } + case ACTION_MPD_SEARCH: + { + line_editor_start (&g.editor, '/'); + g.editor.on_changed = search_on_changed; + g.editor.on_end = search_on_end; + + // We just need to be deeper but not match anything real, + // in order to keep the rest of the codebase functional as-is + if (!g_library_tab.searching) + { + char *fake_subdir = xstrdup_printf ("%s/", g_library_tab.path.str); + library_tab_change_level (fake_subdir); + free (fake_subdir); + } + + free (g_library_tab.super.header); + g_library_tab.super.header = xstrdup_printf ("Global search"); + g_library_tab.searching = true; + + // Since we've already changed the header, empty the list, + // although to be consistent we should also ask to search for "", + // which dumps the database + struct strv empty = strv_make (); + library_tab_load_data (&empty); + strv_free (&empty); + + app_invalidate (); + return true; + } + case ACTION_MPD_ADD: + if (!library_tab_is_range_playable (range)) + break; + + for (int i = range.from; i <= range.upto; i++) + { + struct library_tab_item x = + library_tab_resolve (g_library_tab.items.vector[i]); + if (x.type == LIBRARY_DIR || x.type == LIBRARY_FILE) + MPD_SIMPLE ("add", x.path); + } + return true; case ACTION_MPD_REPLACE: if (!library_tab_is_range_playable (range)) break; @@ -2568,18 +2666,6 @@ library_tab_on_action (enum action action) mpd_client_add_task (c, mpd_on_simple_response, NULL); mpd_client_idle (c, 0); return true; - case ACTION_MPD_ADD: - if (!library_tab_is_range_playable (range)) - break; - - for (int i = range.from; i <= range.upto; i++) - { - struct library_tab_item x = - library_tab_resolve (g_library_tab.items.vector[i]); - if (x.type == LIBRARY_DIR || x.type == LIBRARY_FILE) - MPD_SIMPLE ("add", x.path); - } - return true; default: break; }