Code

dbc7adad3c97054e19cf88a13a3de1b9ee39ef1c
[ncmpc.git] / src / screen_browser.c
1 /*
2  * $Id$
3  *
4  * (c) 2004 by Kalle Wallin <kaw@linux.se>
5  * Copyright (C) 2008 Max Kellermann <max@duempel.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
22 #include "screen_browser.h"
23 #include "ncmpc.h"
24 #include "options.h"
25 #include "support.h"
26 #include "strfsong.h"
27 #include "screen_utils.h"
28 #include "gcc.h"
30 #include <string.h>
32 #define USE_OLD_LAYOUT
33 #undef  USE_OLD_ADD
35 #define BUFSIZE 1024
37 #define HIGHLIGHT  (0x01)
39 /* clear the highlight flag for all items in the filelist */
40 static void
41 clear_highlights(mpdclient_filelist_t *fl)
42 {
43         GList *list = g_list_first(fl->list);
45         while( list ) {
46                 filelist_entry_t *entry = list->data;
48                 entry->flags &= ~HIGHLIGHT;
49                 list = list->next;
50         }
51 }
53 /* change the highlight flag for a song */
54 static void
55 set_highlight(mpdclient_filelist_t *fl, mpd_Song *song, int highlight)
56 {
57         GList *list = g_list_first(fl->list);
59         assert(song != NULL);
61         while( list ) {
62                 filelist_entry_t *entry = list->data;
63                 mpd_InfoEntity *entity  = entry->entity;
65                 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
66                         mpd_Song *song2 = entity->info.song;
68                         if( strcmp(song->file, song2->file) == 0 ) {
69                                 if(highlight)
70                                         entry->flags |= HIGHLIGHT;
71                                 else
72                                         entry->flags &= ~HIGHLIGHT;
73                         }
74                 }
75                 list = list->next;
76         }
77 }
79 /* sync highlight flags with playlist */
80 void
81 sync_highlights(mpdclient_t *c, mpdclient_filelist_t *fl)
82 {
83         GList *list = g_list_first(fl->list);
85         while(list) {
86                 filelist_entry_t *entry = list->data;
87                 mpd_InfoEntity *entity = entry->entity;
89                 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
90                         mpd_Song *song = entity->info.song;
92                         if( playlist_get_index_from_file(c, song->file) >= 0 )
93                                 entry->flags |= HIGHLIGHT;
94                         else
95                                 entry->flags &= ~HIGHLIGHT;
96                 }
97                 list=list->next;
98         }
99 }
101 /* the playlist have been updated -> fix highlights */
102 void
103 browser_playlist_changed(struct screen_browser *browser, mpdclient_t *c,
104                          int event, gpointer data)
106         if (browser->filelist == NULL)
107                 return;
109         D("screen_file.c> playlist_callback() [%d]\n", event);
110         switch(event) {
111         case PLAYLIST_EVENT_CLEAR:
112                 clear_highlights(browser->filelist);
113                 break;
114         case PLAYLIST_EVENT_ADD:
115                 set_highlight(browser->filelist, (mpd_Song *) data, 1);
116                 break;
117         case PLAYLIST_EVENT_DELETE:
118                 set_highlight(browser->filelist, (mpd_Song *) data, 0);
119                 break;
120         case PLAYLIST_EVENT_MOVE:
121                 break;
122         default:
123                 sync_highlights(c, browser->filelist);
124                 break;
125         }
128 /* list_window callback */
129 const char *
130 browser_lw_callback(unsigned idx, int *highlight, void *data)
132         static char buf[BUFSIZE];
133         mpdclient_filelist_t *fl = (mpdclient_filelist_t *) data;
134         filelist_entry_t *entry;
135         mpd_InfoEntity *entity;
137         if( (entry=(filelist_entry_t *)g_list_nth_data(fl->list,idx))==NULL )
138                 return NULL;
140         entity = entry->entity;
141         *highlight = (entry->flags & HIGHLIGHT);
143         if( entity == NULL )
144                 return "[..]";
146         if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) {
147                 mpd_Directory *dir = entity->info.directory;
148                 char *directory = utf8_to_locale(basename(dir->path));
150                 g_snprintf(buf, BUFSIZE, "[%s]", directory);
151                 g_free(directory);
152                 return buf;
153         } else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
154                 mpd_Song *song = entity->info.song;
156                 strfsong(buf, BUFSIZE, LIST_FORMAT, song);
157                 return buf;
158         } else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE ) {
159                 mpd_PlaylistFile *plf = entity->info.playlistFile;
160                 char *filename = utf8_to_locale(basename(plf->path));
162 #ifdef USE_OLD_LAYOUT
163                 g_snprintf(buf, BUFSIZE, "*%s*", filename);
164 #else
165                 g_snprintf(buf, BUFSIZE, "<Playlist> %s", filename);
166 #endif
167                 g_free(filename);
168                 return buf;
169         }
171         return "Error: Unknown entry!";
174 /* chdir */
175 int
176 browser_change_directory(struct screen_browser *browser, mpdclient_t *c,
177                          filelist_entry_t *entry, const char *new_path)
179         mpd_InfoEntity *entity = NULL;
180         gchar *path = NULL;
182         if( entry!=NULL )
183                 entity = entry->entity;
184         else if( new_path==NULL )
185                 return -1;
187         if( entity==NULL ) {
188                 if( entry || 0==strcmp(new_path, "..") ) {
189                         /* return to parent */
190                         char *parent = g_path_get_dirname(browser->filelist->path);
191                         if( strcmp(parent, ".") == 0 )
192                                 parent[0] = '\0';
193                         path = g_strdup(parent);
194                         list_window_reset(browser->lw);
195                         /* restore previous list window state */
196                         list_window_pop_state(browser->lw_state, browser->lw);
197                 } else {
198                         /* entry==NULL, then new_path ("" is root) */
199                         path = g_strdup(new_path);
200                         list_window_reset(browser->lw);
201                         /* restore first list window state (pop while returning true) */
202                         while(list_window_pop_state(browser->lw_state, browser->lw));
203                 }
204         } else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY) {
205                 /* enter sub */
206                 mpd_Directory *dir = entity->info.directory;
207                 path = utf8_to_locale(dir->path);
208                 /* save current list window state */
209                 list_window_push_state(browser->lw_state, browser->lw);
210         } else
211                 return -1;
213         mpdclient_filelist_free(browser->filelist);
214         browser->filelist = mpdclient_filelist_get(c, path);
215         sync_highlights(c, browser->filelist);
216         list_window_check_selected(browser->lw, browser->filelist->length);
217         g_free(path);
218         return 0;
221 static int
222 load_playlist(mpdclient_t *c, filelist_entry_t *entry)
224         mpd_InfoEntity *entity = entry->entity;
225         mpd_PlaylistFile *plf = entity->info.playlistFile;
226         char *filename = utf8_to_locale(plf->path);
228         if (mpdclient_cmd_load_playlist_utf8(c, plf->path) == 0)
229                 screen_status_printf(_("Loading playlist %s..."), basename(filename));
230         g_free(filename);
231         return 0;
234 static int
235 enqueue_and_play(mpdclient_t *c, filelist_entry_t *entry)
237         int idx;
238         mpd_InfoEntity *entity = entry->entity;
239         mpd_Song *song = entity->info.song;
241         if (!(entry->flags & HIGHLIGHT)) {
242                 if (mpdclient_cmd_add(c, song) == 0) {
243                         char buf[BUFSIZE];
245                         entry->flags |= HIGHLIGHT;
246                         strfsong(buf, BUFSIZE, LIST_FORMAT, song);
247                         screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
248                         mpdclient_update(c); /* get song id */
249                 } else
250                         return -1;
251         }
253         idx = playlist_get_index_from_file(c, song->file);
254         mpdclient_cmd_play(c, idx);
255         return 0;
258 int
259 browser_handle_enter(struct screen_browser *browser, mpdclient_t *c)
261         filelist_entry_t *entry;
262         mpd_InfoEntity *entity;
264         if (browser->filelist == NULL)
265                 return -1;
266         entry = (filelist_entry_t *) g_list_nth_data(browser->filelist->list,
267                                                      browser->lw->selected);
268         if( entry==NULL )
269                 return -1;
271         entity = entry->entity;
272         if (entity == NULL || entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY)
273                 return browser_change_directory(browser, c, entry, NULL);
274         else if (entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE)
275                 return load_playlist(c, entry);
276         else if (entity->type == MPD_INFO_ENTITY_TYPE_SONG)
277                 return enqueue_and_play(c, entry);
278         return -1;
282 #ifdef USE_OLD_ADD
283 /* NOTE - The add_directory functions should move to mpdclient.c */
284 extern gint mpdclient_finish_command(mpdclient_t *c);
286 static int
287 add_directory(mpdclient_t *c, char *dir)
289         mpd_InfoEntity *entity;
290         GList *subdir_list = NULL;
291         GList *list = NULL;
292         char *dirname;
294         dirname = utf8_to_locale(dir);
295         screen_status_printf(_("Adding directory %s...\n"), dirname);
296         doupdate();
297         g_free(dirname);
298         dirname = NULL;
300         mpd_sendLsInfoCommand(c->connection, dir);
301         mpd_sendCommandListBegin(c->connection);
302         while( (entity=mpd_getNextInfoEntity(c->connection)) ) {
303                 if( entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
304                         mpd_Song *song = entity->info.song;
305                         mpd_sendAddCommand(c->connection, song->file);
306                         mpd_freeInfoEntity(entity);
307                 } else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) {
308                         subdir_list = g_list_append(subdir_list, (gpointer) entity);
309                 } else
310                         mpd_freeInfoEntity(entity);
311         }
312         mpd_sendCommandListEnd(c->connection);
313         mpdclient_finish_command(c);
314         c->need_update = TRUE;
316         list = g_list_first(subdir_list);
317         while( list!=NULL ) {
318                 mpd_Directory *dir;
320                 entity = list->data;
321                 dir = entity->info.directory;
322                 add_directory(c, dir->path);
323                 mpd_freeInfoEntity(entity);
324                 list->data=NULL;
325                 list=list->next;
326         }
327         g_list_free(subdir_list);
328         return 0;
330 #endif
332 static int
333 browser_select_entry(mpdclient_t *c, filelist_entry_t *entry,
334                      gboolean toggle)
336         assert(entry != NULL);
337         assert(entry->entity != NULL);
339         if (entry->entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE)
340                 return load_playlist(c, entry);
342         if (entry->entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
343                 mpd_Directory *dir = entry->entity->info.directory;
344 #ifdef USE_OLD_ADD
345                 add_directory(c, tmp);
346 #else
347                 if (mpdclient_cmd_add_path_utf8(c, dir->path) == 0) {
348                         char *tmp = utf8_to_locale(dir->path);
350                         screen_status_printf(_("Adding \'%s\' to playlist\n"), tmp);
351                         g_free(tmp);
352                 }
353 #endif
354                 return 0;
355         }
357         if (entry->entity->type != MPD_INFO_ENTITY_TYPE_SONG)
358                 return -1;
360         assert(entry->entity->info.song != NULL);
362         if (!toggle || (entry->flags & HIGHLIGHT) == 0) {
363                 mpd_Song *song = entry->entity->info.song;
365                 entry->flags |= HIGHLIGHT;
367                 if (mpdclient_cmd_add(c, song) == 0) {
368                         char buf[BUFSIZE];
370                         strfsong(buf, BUFSIZE, LIST_FORMAT, song);
371                         screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
372                 }
373         } else {
374                 /* remove song from playlist */
375                 mpd_Song *song = entry->entity->info.song;
376                 int idx;
378                 entry->flags &= ~HIGHLIGHT;
380                 while ((idx = playlist_get_index_from_file(c, song->file)) >=0)
381                         mpdclient_cmd_delete(c, idx);
382         }
384         return 0;
387 int
388 browser_handle_select(struct screen_browser *browser, mpdclient_t *c)
390         filelist_entry_t *entry;
392         if (browser->filelist == NULL)
393                 return -1;
394         entry = g_list_nth_data(browser->filelist->list, browser->lw->selected);
395         if (entry == NULL || entry->entity == NULL)
396                 return -1;
398         return browser_select_entry(c, entry, TRUE);
401 void
402 browser_handle_select_all(struct screen_browser *browser, mpdclient_t *c)
404         filelist_entry_t *entry;
405         GList *temp = browser->filelist->list;
407         if (browser->filelist == NULL)
408                 return;
410         for (browser->filelist->list = g_list_first(browser->filelist->list);
411              browser->filelist->list;
412              browser->filelist->list = g_list_next(browser->filelist->list)) {
413                 entry = browser->filelist->list->data;
414                 if (entry != NULL && entry->entity != NULL)
415                         browser_select_entry(c, entry, FALSE);
416         }
418         browser->filelist->list = temp;
421 #ifdef HAVE_GETMOUSE
422 int
423 browser_handle_mouse_event(struct screen_browser *browser, mpdclient_t *c)
425         int row;
426         unsigned prev_selected = browser->lw->selected;
427         unsigned long bstate;
428         int length;
430         if (browser->filelist)
431                 length = browser->filelist->length;
432         else
433                 length = 0;
435         if( screen_get_mouse_event(c, browser->lw, length, &bstate, &row) )
436                 return 1;
438         browser->lw->selected = browser->lw->start + row;
439         list_window_check_selected(browser->lw, length);
441         if( bstate & BUTTON1_CLICKED ) {
442                 if (prev_selected == browser->lw->selected)
443                         browser_handle_enter(browser, c);
444         } else if (bstate & BUTTON3_CLICKED) {
445                 if (prev_selected == browser->lw->selected)
446                         browser_handle_select(browser, c);
447         }
449         return 1;
451 #endif