Code

cmd_select_all added
[ncmpc.git] / src / screen_search.c
1 /* 
2  * $Id$
3  *
4  * (c) 2004 by Kalle Wallin <kaw@linux.se>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <glib.h>
25 #include <ncurses.h>
27 #include "config.h"
28 #ifndef DISABLE_SEARCH_SCREEN
29 #include "ncmpc.h"
30 #include "options.h"
31 #include "support.h"
32 #include "mpdclient.h"
33 #include "strfsong.h"
34 #include "command.h"
35 #include "screen.h"
36 #include "utils.h"
37 #include "screen_utils.h"
38 #include "screen_browse.h"
40 /* new search stuff with qball's libmpdclient */
41 #define FUTURE
44 #ifdef FUTURE
46 extern gint mpdclient_finish_command(mpdclient_t *c);
48 typedef struct
49 {
50   int id;
51   char *name;
52   char *localname;
53 } search_tag_t;
55 static search_tag_t search_tag[] = {
56   { MPD_TAG_ITEM_ARTIST,   "artist",    N_("artist") },
57   { MPD_TAG_ITEM_ALBUM,    "album",     N_("album") },
58   { MPD_TAG_ITEM_TITLE,    "title",     N_("title") },
59   { MPD_TAG_ITEM_TRACK,    "track",     N_("track") },
60   { MPD_TAG_ITEM_NAME,     "name",      N_("name") },
61   { MPD_TAG_ITEM_GENRE,    "genre",     N_("genre") },
62   { MPD_TAG_ITEM_DATE,     "date",      N_("date") },
63   { MPD_TAG_ITEM_COMPOSER, "composer",  N_("composer") },
64   { MPD_TAG_ITEM_PERFORMER,"performer", N_("performer") },
65   { MPD_TAG_ITEM_COMMENT,  "comment",   N_("comment") },
66   { MPD_TAG_ITEM_FILENAME, "filename",  N_("file") },
67   { -1,                    NULL,        NULL }
68 };
70 static int
71 search_get_tag_id(char *name)
72 {
73   int i;
75   i=0;
76   while( search_tag[i].name )
77     {
78       if( strcasecmp(search_tag[i].name, name)==0 || 
79           strcasecmp(search_tag[i].localname, name)==0 )
80         return search_tag[i].id;
81       i++;
82     }
83   return -1;
84 }
86 #endif
89 #define SEARCH_TITLE    0
90 #define SEARCH_ARTIST   1
91 #define SEARCH_ALBUM    2
92 #define SEARCH_FILE     3
94 #define SEARCH_ARTIST_TITLE 999
96 typedef struct {
97   int table;
98   char *label;
99 } search_type_t;
101 static search_type_t mode[] = {
102   { MPD_TABLE_TITLE,     N_("Title") },
103   { MPD_TABLE_ARTIST,    N_("Artist") },
104   { MPD_TABLE_ALBUM,     N_("Album") },
105   { MPD_TABLE_FILENAME,  N_("Filename") },
106   { SEARCH_ARTIST_TITLE, N_("Artist + Title") },
107   { 0, NULL }
108 };
110 static list_window_t *lw = NULL;
111 static mpdclient_filelist_t *filelist = NULL;
112 static GList *search_history = NULL;
113 static gchar *pattern = NULL;
114 static gboolean advanced_search_mode = FALSE;
117 /* search info */
118 static char *
119 lw_search_help_callback(int index, int *highlight, void *data)
121   int text_rows;
122   static char *text[] = {
123     "Quick  - just enter a string and ncmpc will search according",
124     "               to the current search mode (displayed above).",
125     "",
126     "Advanced  -  <tag>:<search term> [<tag>:<search term>...]",
127     "                   Example: artist:radiohead album:pablo honey",
128     "",
129     "              avalible tags: artist, album, title, track,", 
130     "              name, genre, date composer, performer, comment, file",
131     "",
132     NULL
133   };
135   text_rows=0;
136   while( text[text_rows] )
137     text_rows++;
138   
139   if( index < text_rows )
140     return text[index];
141   return NULL;
144 /* the playlist have been updated -> fix highlights */
145 static void 
146 playlist_changed_callback(mpdclient_t *c, int event, gpointer data)
148   if( filelist==NULL )
149     return;
150   D("screen_search.c> playlist_callback() [%d]\n", event);
151   switch(event)
152     {
153     case PLAYLIST_EVENT_CLEAR:
154       clear_highlights(filelist);
155       break;
156     default:
157       sync_highlights(c, filelist);
158       break;
159     }
162 /* sanity check search mode value */
163 static void
164 search_check_mode(void)
166   int max = 0;
168   while( mode[max].label != NULL )
169     max++;
170   if( options.search_mode<0 )
171     options.search_mode = 0;
172   else if( options.search_mode>=max )
173     options.search_mode = max-1;
176 static void
177 search_clear(screen_t *screen, mpdclient_t *c, gboolean clear_pattern)
179   if( filelist )
180     {
181       mpdclient_remove_playlist_callback(c, playlist_changed_callback);
182       filelist = mpdclient_filelist_free(filelist);
183     }
184   if( clear_pattern && pattern )
185     {
186       g_free(pattern);
187       pattern = NULL;
188     }
191 #ifdef FUTURE
192 mpdclient_filelist_t *
193 filelist_search(mpdclient_t *c, int exact_match, int table, gchar *pattern)
195   mpdclient_filelist_t *list, *list2;
197   if( table == SEARCH_ARTIST_TITLE )
198     {
199       list = mpdclient_filelist_search(c, FALSE, MPD_TABLE_ARTIST, pattern);
200       list2 = mpdclient_filelist_search(c, FALSE, MPD_TABLE_TITLE, pattern);
202       list->length += list2->length;
203       list->list = g_list_concat(list->list, list2->list);
204       list->list = g_list_sort(list->list, compare_filelistentry_format);
205       list->updated = TRUE;
206     }
207   else
208     {
209       list = mpdclient_filelist_search(c, FALSE, table, pattern);
210     }
212   return list;
215 /*-----------------------------------------------------------------------
216  * NOTE: This code exists to test a new search ui,
217  *       Its ugly and MUST be redesigned before the next release!
218  *-----------------------------------------------------------------------
219  */
220 static mpdclient_filelist_t *
221 search_advanced_query(char *query, mpdclient_t *c)
223   int i,j;
224   char **strv;
225   int table[10];
226   char *arg[10];
227   mpdclient_filelist_t *filelist = NULL;
229   advanced_search_mode = FALSE;
230   if( g_strrstr(query, ":") == NULL )
231     return NULL;
232   
233   strv = g_strsplit_set(query, ": ", 0);
235   i=0;
236   while( strv[i] )
237     {
238       D("strv[%d] = \"%s\"\n", i, strv[i]);
239       i++;
240     }
242   memset(table, 0, 10*sizeof(int));
243   memset(arg,   0, 10*sizeof(char *));
245   i=0;
246   j=0;
247   while( strv[i] && strlen(strv[i])>0 && i<9 )
248     {
249       D("strv[%d] = \"%s\"\n", i, strv[i]);
251       int id = search_get_tag_id(strv[i]);
252       if( id==-1 )
253         {
254           if( table[j] )
255             {
256               char *tmp = arg[j];
257               arg[j] = g_strdup_printf("%s %s", arg[j], strv[i]);
258               g_free(tmp);
259             }
260           else
261             {
262               D("Bad search tag %s\n", strv[i]);
263               screen_status_printf(_("Bad search tag %s"), strv[i]);
264             }
265           i++;
266         }
267       else if( strv[i+1] == NULL || strlen(strv[i+1])==0 )
268         {
269           D("No argument for search tag %s\n", strv[i]);
270           screen_status_printf(_("No argument for search tag %s"), strv[i]);
271           i++;
272           //      j--;
273           //table[j] = -1;
274         }
275       else
276         {
277           table[j] = id;
278           arg[j] = locale_to_utf8(strv[i+1]); // FREE ME
279           j++;
280           table[j] = -1;
281           arg[j] = NULL;
282           i = i + 2;
283           advanced_search_mode = TRUE;
284         }     
285     }
287   g_strfreev(strv);
290   if( advanced_search_mode && j>0 )
291     {
292       /*-----------------------------------------------------------------------
293        * NOTE (again): This code exists to test a new search ui,
294        *               Its ugly and MUST be redesigned before the next release!
295        *             + the code below should live in mpdclient.c
296        *-----------------------------------------------------------------------
297        */
298       /** stupid - but this is just a test...... (fulhack)  */
299           mpd_startSearch(c->connection, FALSE);
301           int iter;
302           for(iter = 0; iter < 10; iter++)
303           {
304                   mpd_addConstraintSearch(c->connection, table[iter], arg[iter]);
305           }               
306                         
307           mpd_commitSearch(c->connection);
308           
309       filelist = g_malloc0(sizeof(mpdclient_filelist_t));
311       mpd_InfoEntity *entity;
313       while( (entity=mpd_getNextInfoEntity(c->connection)) ) 
314         {
315           filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
316       
317           entry->entity = entity;
318           filelist->list = g_list_append(filelist->list, (gpointer) entry);
319           filelist->length++;
320         }
321   
322       if( mpdclient_finish_command(c) && filelist )
323         filelist = mpdclient_filelist_free(filelist);
325       filelist->updated = TRUE;
326     } 
327   
328   i=0;
329   while( arg[i] )
330     g_free(arg[i++]);
332   return filelist;
334 #else
335 #define search_advanced_query(pattern,c) (NULL)
336 #endif
338 static void
339 search_new(screen_t *screen, mpdclient_t *c)
341   search_clear(screen, c, TRUE);
342   
343   pattern = screen_readln(screen->status_window.w, 
344                           _("Search: "),
345                           NULL,
346                           &search_history,
347                           NULL);
349   if( pattern && strcmp(pattern,"")==0 )
350     {
351       g_free(pattern);
352       pattern=NULL;
353     }
354   
355   if( pattern==NULL )
356     {
357       list_window_reset(lw);
358       return;
359     }
361   if( !MPD_VERSION_LT(c, 0, 12, 0) )
362     filelist = search_advanced_query(pattern, c);
363   if( !advanced_search_mode && filelist==NULL )
364     filelist = filelist_search(c, 
365                                FALSE,
366                                mode[options.search_mode].table,
367                                pattern);
368   sync_highlights(c, filelist);
369   mpdclient_install_playlist_callback(c, playlist_changed_callback);
370   list_window_check_selected(lw, filelist->length);
375 static void
376 init(WINDOW *w, int cols, int rows)
378   lw = list_window_init(w, cols, rows);
381 static void
382 quit(void)
384   if( search_history )
385     string_list_free(search_history);
386   if( filelist )
387     filelist = mpdclient_filelist_free(filelist);
388   list_window_free(lw);
389   if( pattern )
390     g_free(pattern);
391   pattern = NULL;
394 static void
395 open(screen_t *screen, mpdclient_t *c)
397   //  if( pattern==NULL )
398   //    search_new(screen, c);
399   // else
400   screen_status_printf(_("Press %s for a new search"),
401                          get_key_names(CMD_SCREEN_SEARCH,0));
402   search_check_mode();
405 static void
406 resize(int cols, int rows)
408   lw->cols = cols;
409   lw->rows = rows;
412 static void
413 close(void)
417 static void 
418 paint(screen_t *screen, mpdclient_t *c)
420   lw->clear = 1;
421   
422   if( filelist )
423     {
424       lw->flags = 0;
425       list_window_paint(lw, browse_lw_callback, (void *) filelist);
426       filelist->updated = FALSE;
427     }
428   else
429     {
430       lw->flags = LW_HIDE_CURSOR;
431       list_window_paint(lw, lw_search_help_callback, NULL);
432       if( !MPD_VERSION_LT(c, 0, 12, 0) )
433         g_strdup_printf("Advanced search disabled (MPD version < 0.12.0"); 
434       //      wmove(lw->w, 0, 0);
435       //wclrtobot(lw->w);
436     }
437   wnoutrefresh(lw->w);
440 static void 
441 update(screen_t *screen, mpdclient_t *c)
443   if( filelist==NULL || filelist->updated )
444     {
445       paint(screen, c);
446       return;
447     }
448   list_window_paint(lw, browse_lw_callback, (void *) filelist);
449   wnoutrefresh(lw->w);
452 static char *
453 get_title(char *str, size_t size)
455   if( advanced_search_mode && pattern )
456     g_snprintf(str, size, _("Search: %s"), pattern);
457   else if( pattern )
458     g_snprintf(str, size, 
459                _("Search: Results for %s [%s]"), 
460                pattern,
461                _(mode[options.search_mode].label));
462   else
463     g_snprintf(str, size, _("Search: Press %s for a new search [%s]"),
464                get_key_names(CMD_SCREEN_SEARCH,0),
465                _(mode[options.search_mode].label));
466                
467   return str;
470 static list_window_t *
471 get_filelist_window()
473   return lw;
476 static int 
477 search_cmd(screen_t *screen, mpdclient_t *c, command_t cmd)
479   switch(cmd)
480     {
481     case CMD_PLAY:
482        browse_handle_enter(screen, c, lw, filelist);
483       return 1;
485     case CMD_SELECT:
486       if( browse_handle_select(screen, c, lw, filelist) == 0 )
487         {
488           /* continue and select next item... */
489           cmd = CMD_LIST_NEXT;
490         }
491       /* call list_window_cmd to go to the next item */
492       return list_window_cmd(lw, filelist->length, cmd);
494     case CMD_SELECT_ALL:
495       browse_handle_select_all (screen, c, lw, filelist);
496       paint (screen, c);
497       return 0;
499     case CMD_SEARCH_MODE:
500       options.search_mode++;
501       if( mode[options.search_mode].label == NULL )
502         options.search_mode = 0;
503       screen_status_printf(_("Search mode: %s"), 
504                            _(mode[options.search_mode].label));
505       /* continue and update... */
506     case CMD_SCREEN_UPDATE:
507       if( pattern )
508         {
509           search_clear(screen, c, FALSE);
510           filelist = filelist_search(c, 
511                                      FALSE,
512                                      mode[options.search_mode].table,
513                                      pattern);
514           sync_highlights(c, filelist);
515         }
516       return 1;
518     case CMD_SCREEN_SEARCH:
519       search_new(screen, c);
520       return 1;
522     case CMD_CLEAR:
523       search_clear(screen, c, TRUE);
524       list_window_reset(lw);
525       return 1;
527     case CMD_LIST_FIND:
528     case CMD_LIST_RFIND:
529     case CMD_LIST_FIND_NEXT:
530     case CMD_LIST_RFIND_NEXT:
531       if( filelist )
532         return screen_find(screen, c, 
533                            lw, filelist->length,
534                            cmd, browse_lw_callback, (void *) filelist);
535       else
536         return 1;
538     case CMD_MOUSE_EVENT:
539       return browse_handle_mouse_event(screen,c,lw,filelist);
541     default:
542       if( filelist )
543         return list_window_cmd(lw, filelist->length, cmd);
544     }
545   
546   return 0;
549 screen_functions_t *
550 get_screen_search(void)
552   static screen_functions_t functions;
554   memset(&functions, 0, sizeof(screen_functions_t));
555   functions.init   = init;
556   functions.exit   = quit;
557   functions.open   = open;
558   functions.close  = close;
559   functions.resize = resize;
560   functions.paint  = paint;
561   functions.update = update;
562   functions.cmd    = search_cmd;
563   functions.get_lw = get_filelist_window;
564   functions.get_title = get_title;
566   return &functions;
570 #endif /* ENABLE_SEARCH_SCREEN */