diff options
-rw-r--r-- | src/libmpdclient.c | 166 | ||||
-rw-r--r-- | src/libmpdclient.h | 33 | ||||
-rw-r--r-- | src/screen_search.c | 255 |
3 files changed, 433 insertions, 21 deletions
diff --git a/src/libmpdclient.c b/src/libmpdclient.c index bc1c1d6e7..a8bbabef7 100644 --- a/src/libmpdclient.c +++ b/src/libmpdclient.c @@ -31,6 +31,7 @@ */ + #include "libmpdclient.h" #include <errno.h> @@ -39,12 +40,16 @@ #include <netdb.h> #include <stdio.h> #include <sys/param.h> + + #include <string.h> +#include <strings.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> +#include <stdarg.h> #ifndef MPD_NO_IPV6 #ifdef AF_INET6 @@ -59,6 +64,22 @@ #define COMMAND_LIST 1 #define COMMAND_LIST_OK 2 +char * mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] = +{ + "Artist", + "Album", + "Title", + "Track", + "Name", + "Genre", + "Date", + "Composer", + "Performer", + "Comment", + "filename" +}; + + #ifdef MPD_HAVE_IPV6 int mpd_ipv6Supported() { int s; @@ -105,8 +126,8 @@ mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value) { mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement)); - ret->name = strdup(name); - ret->value = strdup(value); + ret->name = (char *)strdup(name); + ret->value = (char *)strdup(value); return ret; } @@ -1073,6 +1094,13 @@ char * mpd_getNextReturnElementNamed(mpd_Connection * connection, return NULL; } +char * mpd_getNextTag(mpd_Connection * connection,int table) { + if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES) + { + return mpd_getNextReturnElementNamed(connection,mpdTagItemKeys[table]); + } + return NULL; +} char * mpd_getNextArtist(mpd_Connection * connection) { return mpd_getNextReturnElementNamed(connection,"Artist"); } @@ -1154,6 +1182,89 @@ void mpd_sendSearchCommand(mpd_Connection * connection, int table, free(string); free(sanitStr); } +void mpd_sendSearchTagCommand(mpd_Connection *connection, ...) +{ + va_list arglist; + va_start(arglist, connection); + mpd_sendVSearchTagCommand(connection, arglist); + va_end(arglist); +} + +void mpd_sendVSearchTagCommand(mpd_Connection * connection, va_list arglist) +{ + char *st, *str; + char * string=NULL; + int table; + char * sanitStr; + string = realloc(string,strlen("search")+1); + strcpy(string, "search"); + while((table = va_arg(arglist, int)) != -1) + { + if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES) + { + st = mpdTagItemKeys[table]; + str = va_arg(arglist,char *); + sanitStr = mpd_sanitizeArg(str); + string = realloc(string, strlen(string)+strlen(st)+strlen(sanitStr)+6); + sprintf(string, "%s %s \"%s\"",string,st,sanitStr); + free(sanitStr); + } + else { + connection->error = 1; + sprintf(connection->errorStr,"unknown table for search %i",table); + va_end(arglist); + return; + } + } + /* set the last to '\n', should be sufficient space in the string for this */ + sprintf(string, "%s\n", string); + mpd_sendInfoCommand(connection,string); + free(string); +} + + +void mpd_sendFindTagCommand(mpd_Connection *connection, ...) +{ + va_list arglist; + va_start(arglist, connection); + mpd_sendVFindTagCommand(connection, arglist); + va_end(arglist); +} + + + + +void mpd_sendVFindTagCommand(mpd_Connection * connection, va_list arglist) +{ + char *st, *str; + char * string=NULL; + int table; + char * sanitStr; + string = realloc(string,strlen("find")+1); + strcpy(string, "find"); + while((table = va_arg(arglist, int)) != -1) + { + if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES) + { + st = mpdTagItemKeys[table]; + str = va_arg(arglist,char *); + sanitStr = mpd_sanitizeArg(str); + string = realloc(string, strlen(string)+strlen(st)+strlen(sanitStr)+6); + sprintf(string, "%s %s \"%s\"",string,st,sanitStr); + free(sanitStr); + } + else { + connection->error = 1; + sprintf(connection->errorStr,"unknown table for find %i",table); + va_end(arglist); + return; + } + } + /* set the last to '\n', should be sufficient space in the string for this */ + sprintf(string, "%s\n", string); + mpd_sendInfoCommand(connection,string); + free(string); +} void mpd_sendFindCommand(mpd_Connection * connection, int table, const char * str) @@ -1202,6 +1313,53 @@ void mpd_sendListCommand(mpd_Connection * connection, int table, free(string); } +void mpd_sendListTagCommand(mpd_Connection *connection, int ret_table, ...) +{ + va_list arglist; + va_start(arglist, ret_table); + mpd_sendVListTagCommand(connection, ret_table, arglist); + va_end(arglist); +} +void mpd_sendVListTagCommand(mpd_Connection * connection,int ret_table, va_list arglist) +{ + char *st, *str; + char * string=NULL; + int table; + char * sanitStr; + if(ret_table < 0 && ret_table >= MPD_TAG_NUM_OF_ITEM_TYPES) + { + connection->error = 1; + sprintf(connection->errorStr,"unknown ret_table for search %i",ret_table); + return; + } + + string = realloc(string,strlen("list")+3+strlen(mpdTagItemKeys[ret_table])); + sprintf(string, "list %s",mpdTagItemKeys[ret_table]); + while((table = va_arg(arglist, int)) != -1) + { + if(table >= 0 && table < MPD_TAG_NUM_OF_ITEM_TYPES) + { + st = mpdTagItemKeys[table]; + str = va_arg(arglist,char *); + sanitStr = mpd_sanitizeArg(str); + string = realloc(string, strlen(string)+strlen(st)+strlen(sanitStr)+7); + sprintf(string, "%s %s \"%s\"",string,st,sanitStr); + free(sanitStr); + } + else { + connection->error = 1; + sprintf(connection->errorStr,"unknown table for search %i",table); + va_end(arglist); + return; + } + } + /* set the last to '\n', should be sufficient space in the string for this */ + sprintf(string,"%s\n", string); + mpd_sendInfoCommand(connection,string); + free(string); +} + + void mpd_sendAddCommand(mpd_Connection * connection, const char * file) { char * sFile = mpd_sanitizeArg(file); char * string = malloc(strlen("add")+strlen(sFile)+5); @@ -1440,7 +1598,7 @@ mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) { mpd_OutputEntity * output = NULL; if(connection->doneProcessing || (connection->listOks && - connection->doneListOk)) + connection->doneListOk)) { return NULL; } @@ -1472,7 +1630,7 @@ mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) { free(output); return NULL; } - + } return output; diff --git a/src/libmpdclient.h b/src/libmpdclient.h index 79a94d8ee..dbbd1019e 100644 --- a/src/libmpdclient.h +++ b/src/libmpdclient.h @@ -35,7 +35,7 @@ #define LIBMPDCLIENT_H #include <sys/time.h> - +#include <stdarg.h> #define MPD_BUFFER_MAX_LENGTH 50000 #define MPD_WELCOME_MESSAGE "OK MPD " @@ -71,6 +71,26 @@ extern "C" { #endif + +enum +{ + MPD_TAG_ITEM_ARTIST, + MPD_TAG_ITEM_ALBUM, + MPD_TAG_ITEM_TITLE, + MPD_TAG_ITEM_TRACK, + MPD_TAG_ITEM_NAME, + MPD_TAG_ITEM_GENRE, + MPD_TAG_ITEM_DATE, + MPD_TAG_ITEM_COMPOSER, + MPD_TAG_ITEM_PERFORMER, + MPD_TAG_ITEM_COMMENT, + MPD_TAG_ITEM_FILENAME, + MPD_TAG_NUM_OF_ITEM_TYPES +}mpd_TagItems; + + +extern char * mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES]; + /* internal stuff don't touch this struct */ typedef struct _mpd_ReturnElement { char * name; @@ -388,21 +408,32 @@ void mpd_sendSearchCommand(mpd_Connection * connection, int table, void mpd_sendFindCommand(mpd_Connection * connection, int table, const char * str); + +void mpd_sendSearchTagCommand(mpd_Connection *connection, ...); +void mpd_sendFindTagCommand(mpd_Connection *connection, ...); +void mpd_sendVSearchTagCommand(mpd_Connection *connection, va_list arglist); +void mpd_sendVFindTagCommand(mpd_Connection *connection, va_list arglist); /* LIST TAG COMMANDS */ /* use this function fetch next artist entry, be sure to free the returned * string. NULL means there are no more. Best used with sendListArtists */ + char * mpd_getNextArtist(mpd_Connection * connection); char * mpd_getNextAlbum(mpd_Connection * connection); +char * mpd_getNextTag(mpd_Connection *connection, int table); + /* list artist or albums by artist, arg1 should be set to the artist if * listing albums by a artist, otherwise NULL for listing all artists or albums */ void mpd_sendListCommand(mpd_Connection * connection, int table, const char * arg1); +void mpd_sendListTagCommand(mpd_Connection * connection, int table,...); +void mpd_sendVListTagCommand(mpd_Connection * connection,int ret_table, va_list arglist); + /* SIMPLE COMMANDS */ void mpd_sendAddCommand(mpd_Connection * connection, const char * file); diff --git a/src/screen_search.c b/src/screen_search.c index 03ce11ad6..ebeb25592 100644 --- a/src/screen_search.c +++ b/src/screen_search.c @@ -33,13 +33,61 @@ #include "strfsong.h" #include "command.h" #include "screen.h" +#include "utils.h" #include "screen_utils.h" #include "screen_browse.h" -#define SEARCH_TITLE 0 -#define SEARCH_ARTIST 1 -#define SEARCH_ALBUM 2 -#define SEARCH_FILE 3 +/* new search stuff with qball's libmpdclient */ +#define FUTURE + + +#ifdef FUTURE + +typedef struct +{ + int id; + char *name; + char *localname; +} search_tag_t; + +static search_tag_t search_tag[] = { + { MPD_TAG_ITEM_ARTIST, "artist", N_("artist") }, + { MPD_TAG_ITEM_ALBUM, "album", N_("album") }, + { MPD_TAG_ITEM_TITLE, "title", N_("title") }, + { MPD_TAG_ITEM_TRACK, "track", N_("track") }, + { MPD_TAG_ITEM_NAME, "name", N_("name") }, + { MPD_TAG_ITEM_GENRE, "genre", N_("genre") }, + { MPD_TAG_ITEM_DATE, "date", N_("date") }, + { MPD_TAG_ITEM_COMPOSER, "composer", N_("composer") }, + { MPD_TAG_ITEM_PERFORMER,"performer", N_("performer") }, + { MPD_TAG_ITEM_COMMENT, "comment", N_("comment") }, + { MPD_TAG_ITEM_FILENAME, "filename", N_("file") }, + { -1, NULL, NULL } +}; + +static int +search_get_tag_id(char *name) +{ + int i; + + i=0; + while( search_tag[i].name ) + { + if( strcasecmp(search_tag[i].name, name)==0 || + strcasecmp(search_tag[i].localname, name)==0 ) + return search_tag[i].id; + i++; + } + return -1; +} + +#endif + + +#define SEARCH_TITLE 0 +#define SEARCH_ARTIST 1 +#define SEARCH_ALBUM 2 +#define SEARCH_FILE 3 typedef struct { int table; @@ -56,7 +104,44 @@ static search_type_t mode[] = { static list_window_t *lw = NULL; static mpdclient_filelist_t *filelist = NULL; +static GList *search_history = NULL; static gchar *pattern = NULL; +static gboolean advanced_search_mode = FALSE; + + +/* search info */ +static char * +lw_search_help_callback(int index, int *highlight, void *data) +{ + int text_rows; + static char *text[] = { + "Welcome to ncmpc's search screen - SVN version.", + "", + "Quick search - just enter a string and ncmcp will search according", + " to the current search mode (displayed above).", + "", + "Advanced - bla bla bla.... syntax below", + "" + " <tag>:<search term> [<tag>:<search term>...]", + "", + "Example: artist:radiohead album:pablo honey", + "", + "##### SOMEONE - Write a proper help text, please! #####", + "", + "avalible tags: artist, album, title, track, name, genre, date", + " composer, performer, comment, file", + "", + NULL + }; + + text_rows=0; + while( text[text_rows] ) + text_rows++; + + if( index < text_rows ) + return text[index]; + return NULL; +} /* the playlist have been updated -> fix highlights */ static void @@ -105,6 +190,130 @@ search_clear(screen_t *screen, mpdclient_t *c, gboolean clear_pattern) } } +#ifdef FUTURE +/*----------------------------------------------------------------------- + * NOTE: This code exists to test a new search ui, + * Its ugly and MUST be redesigned before the next release! + *----------------------------------------------------------------------- + */ +static mpdclient_filelist_t * +search_advanced_query(char *query, mpdclient_t *c) +{ + int i,j; + char **strv; + int table[10]; + char *arg[10]; + mpdclient_filelist_t *filelist = NULL; + + advanced_search_mode = FALSE; + if( g_strrstr(query, ":") == NULL ) + return NULL; + + strv = g_strsplit_set(query, ": ", 0); + + i=0; + while( strv[i] ) + { + D("strv[%d] = \"%s\"\n", i, strv[i]); + i++; + } + + memset(table, 0, 10*sizeof(int)); + memset(arg, 0, 10*sizeof(char *)); + + i=0; + j=0; + while( strv[i] && i<9 ) + { + D("strv[%d] = \"%s\"\n", i, strv[i]); + + int id = search_get_tag_id(strv[i]); + if( id==-1 ) + { + if( table[j] ) + { + char *tmp = arg[j]; + arg[j] = g_strdup_printf("%s %s", arg[j], strv[i]); + g_free(tmp); + } + else + { + D("Bad search tag %s\n", strv[i]); + screen_status_printf(_("Bad search tag %s"), strv[i]); + } + i++; + } + else if( strv[i+1] == NULL ) + { + D("No argument for search tag %s\n", strv[i]); + screen_status_printf(_("No argument for search tag %s"), strv[i]); + i++; + } + else + { + table[j] = id; + arg[j] = locale_to_utf8(strv[i+1]); // FREE ME + j++; + table[j] = -1; + arg[j] = NULL; + i = i + 2; + advanced_search_mode = TRUE; + } + } + + g_strfreev(strv); + + + if( advanced_search_mode ) + { + /*----------------------------------------------------------------------- + * NOTE (again): This code exists to test a new search ui, + * Its ugly and MUST be redesigned before the next release! + * + the code below should live in mpdclient.c + *----------------------------------------------------------------------- + */ + mpd_InfoEntity *entity; + + /** stupid - but this is just a test...... (fulhack) */ + mpd_sendSearchTagCommand(c->connection, + table[0], arg[0], + table[1], arg[1], + table[2], arg[2], + table[3], arg[3], + table[4], arg[4], + table[5], arg[5], + table[6], arg[6], + table[7], arg[7], + table[8], arg[8], + table[9], arg[9]); + + filelist = g_malloc0(sizeof(mpdclient_filelist_t)); + + while( (entity=mpd_getNextInfoEntity(c->connection)) ) + { + filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t)); + + entry->entity = entity; + filelist->list = g_list_append(filelist->list, (gpointer) entry); + filelist->length++; + } + + if( mpdclient_finish_command(c) ) + filelist = mpdclient_filelist_free(filelist); + + filelist->updated = TRUE; + } + + i=0; + while( arg[i] ) + g_free(arg[i++]); + + return filelist; +} +#else +#define search_advanced_query(pattern,c) (NULL) +#endif + static void search_new(screen_t *screen, mpdclient_t *c) { @@ -113,7 +322,7 @@ search_new(screen_t *screen, mpdclient_t *c) pattern = screen_readln(screen->status_window.w, _("Search: "), NULL, - NULL, + &search_history, NULL); if( pattern && strcmp(pattern,"")==0 ) @@ -128,15 +337,20 @@ search_new(screen_t *screen, mpdclient_t *c) return; } - filelist = mpdclient_filelist_search(c, - FALSE, - mode[options.search_mode].table, - pattern); + if( !MPD_VERSION_LT(c, 0, 12, 0) ) + filelist = search_advanced_query(pattern, c); + if( !advanced_search_mode && filelist==NULL ) + filelist = mpdclient_filelist_search(c, + FALSE, + mode[options.search_mode].table, + pattern); sync_highlights(c, filelist); mpdclient_install_playlist_callback(c, playlist_changed_callback); list_window_check_selected(lw, filelist->length); } + + static void init(WINDOW *w, int cols, int rows) { @@ -146,6 +360,8 @@ init(WINDOW *w, int cols, int rows) static void quit(void) { + if( search_history ) + string_list_free(search_history); if( filelist ) filelist = mpdclient_filelist_free(filelist); list_window_free(lw); @@ -157,10 +373,10 @@ quit(void) static void open(screen_t *screen, mpdclient_t *c) { - if( pattern==NULL ) - search_new(screen, c); - else - screen_status_printf(_("Press %s for a new search"), + // if( pattern==NULL ) + // search_new(screen, c); + // else + screen_status_printf(_("Press %s for a new search"), get_key_names(CMD_SCREEN_SEARCH,0)); search_check_mode(); } @@ -184,13 +400,18 @@ paint(screen_t *screen, mpdclient_t *c) if( filelist ) { + lw->flags = 0; list_window_paint(lw, browse_lw_callback, (void *) filelist); filelist->updated = FALSE; } else { - wmove(lw->w, 0, 0); - wclrtobot(lw->w); + lw->flags = LW_HIDE_CURSOR; + list_window_paint(lw, lw_search_help_callback, NULL); + if( !MPD_VERSION_LT(c, 0, 12, 0) ) + g_strdup_printf("Advanced search disabled (MPD version < 0.12.0"); + // wmove(lw->w, 0, 0); + //wclrtobot(lw->w); } wnoutrefresh(lw->w); } @@ -210,7 +431,9 @@ update(screen_t *screen, mpdclient_t *c) static char * get_title(char *str, size_t size) { - if( pattern ) + if( advanced_search_mode && pattern ) + g_snprintf(str, size, _("Search: %s"), pattern); + else if( pattern ) g_snprintf(str, size, _("Search: Results for %s [%s]"), pattern, |