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 if( !song )
60 return;
62 while( list ) {
63 filelist_entry_t *entry = list->data;
64 mpd_InfoEntity *entity = entry->entity;
66 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
67 mpd_Song *song2 = entity->info.song;
69 if( strcmp(song->file, song2->file) == 0 ) {
70 if(highlight)
71 entry->flags |= HIGHLIGHT;
72 else
73 entry->flags &= ~HIGHLIGHT;
74 }
75 }
76 list = list->next;
77 }
78 }
80 /* sync highlight flags with playlist */
81 void
82 sync_highlights(mpdclient_t *c, mpdclient_filelist_t *fl)
83 {
84 GList *list = g_list_first(fl->list);
86 while(list) {
87 filelist_entry_t *entry = list->data;
88 mpd_InfoEntity *entity = entry->entity;
90 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
91 mpd_Song *song = entity->info.song;
93 if( playlist_get_index_from_file(c, song->file) >= 0 )
94 entry->flags |= HIGHLIGHT;
95 else
96 entry->flags &= ~HIGHLIGHT;
97 }
98 list=list->next;
99 }
100 }
102 /* the playlist have been updated -> fix highlights */
103 void
104 browser_playlist_changed(struct screen_browser *browser, mpdclient_t *c,
105 int event, gpointer data)
106 {
107 if (browser->filelist == NULL)
108 return;
110 D("screen_file.c> playlist_callback() [%d]\n", event);
111 switch(event) {
112 case PLAYLIST_EVENT_CLEAR:
113 clear_highlights(browser->filelist);
114 break;
115 case PLAYLIST_EVENT_ADD:
116 set_highlight(browser->filelist, (mpd_Song *) data, 1);
117 break;
118 case PLAYLIST_EVENT_DELETE:
119 set_highlight(browser->filelist, (mpd_Song *) data, 0);
120 break;
121 case PLAYLIST_EVENT_MOVE:
122 break;
123 default:
124 sync_highlights(c, browser->filelist);
125 break;
126 }
127 }
129 /* list_window callback */
130 const char *
131 browser_lw_callback(unsigned idx, int *highlight, void *data)
132 {
133 static char buf[BUFSIZE];
134 mpdclient_filelist_t *fl = (mpdclient_filelist_t *) data;
135 filelist_entry_t *entry;
136 mpd_InfoEntity *entity;
138 if( (entry=(filelist_entry_t *)g_list_nth_data(fl->list,idx))==NULL )
139 return NULL;
141 entity = entry->entity;
142 *highlight = (entry->flags & HIGHLIGHT);
144 if( entity == NULL )
145 return "[..]";
147 if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) {
148 mpd_Directory *dir = entity->info.directory;
149 char *directory = utf8_to_locale(basename(dir->path));
151 g_snprintf(buf, BUFSIZE, "[%s]", directory);
152 g_free(directory);
153 return buf;
154 } else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
155 mpd_Song *song = entity->info.song;
157 strfsong(buf, BUFSIZE, LIST_FORMAT, song);
158 return buf;
159 } else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE ) {
160 mpd_PlaylistFile *plf = entity->info.playlistFile;
161 char *filename = utf8_to_locale(basename(plf->path));
163 #ifdef USE_OLD_LAYOUT
164 g_snprintf(buf, BUFSIZE, "*%s*", filename);
165 #else
166 g_snprintf(buf, BUFSIZE, "<Playlist> %s", filename);
167 #endif
168 g_free(filename);
169 return buf;
170 }
172 return "Error: Unknown entry!";
173 }
175 /* chdir */
176 int
177 browser_change_directory(struct screen_browser *browser, mpdclient_t *c,
178 filelist_entry_t *entry, const char *new_path)
179 {
180 mpd_InfoEntity *entity = NULL;
181 gchar *path = NULL;
183 if( entry!=NULL )
184 entity = entry->entity;
185 else if( new_path==NULL )
186 return -1;
188 if( entity==NULL ) {
189 if( entry || 0==strcmp(new_path, "..") ) {
190 /* return to parent */
191 char *parent = g_path_get_dirname(browser->filelist->path);
192 if( strcmp(parent, ".") == 0 )
193 parent[0] = '\0';
194 path = g_strdup(parent);
195 list_window_reset(browser->lw);
196 /* restore previous list window state */
197 list_window_pop_state(browser->lw_state, browser->lw);
198 } else {
199 /* entry==NULL, then new_path ("" is root) */
200 path = g_strdup(new_path);
201 list_window_reset(browser->lw);
202 /* restore first list window state (pop while returning true) */
203 while(list_window_pop_state(browser->lw_state, browser->lw));
204 }
205 } else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY) {
206 /* enter sub */
207 mpd_Directory *dir = entity->info.directory;
208 path = utf8_to_locale(dir->path);
209 /* save current list window state */
210 list_window_push_state(browser->lw_state, browser->lw);
211 } else
212 return -1;
214 mpdclient_filelist_free(browser->filelist);
215 browser->filelist = mpdclient_filelist_get(c, path);
216 sync_highlights(c, browser->filelist);
217 list_window_check_selected(browser->lw, browser->filelist->length);
218 g_free(path);
219 return 0;
220 }
222 static int
223 load_playlist(mpdclient_t *c, filelist_entry_t *entry)
224 {
225 mpd_InfoEntity *entity = entry->entity;
226 mpd_PlaylistFile *plf = entity->info.playlistFile;
227 char *filename = utf8_to_locale(plf->path);
229 if (mpdclient_cmd_load_playlist_utf8(c, plf->path) == 0)
230 screen_status_printf(_("Loading playlist %s..."), basename(filename));
231 g_free(filename);
232 return 0;
233 }
235 static int
236 enqueue_and_play(mpdclient_t *c, filelist_entry_t *entry)
237 {
238 int idx;
239 mpd_InfoEntity *entity = entry->entity;
240 mpd_Song *song = entity->info.song;
242 if (!(entry->flags & HIGHLIGHT)) {
243 if (mpdclient_cmd_add(c, song) == 0) {
244 char buf[BUFSIZE];
246 entry->flags |= HIGHLIGHT;
247 strfsong(buf, BUFSIZE, LIST_FORMAT, song);
248 screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
249 mpdclient_update(c); /* get song id */
250 } else
251 return -1;
252 }
254 idx = playlist_get_index_from_file(c, song->file);
255 mpdclient_cmd_play(c, idx);
256 return 0;
257 }
259 int
260 browser_handle_enter(struct screen_browser *browser, mpdclient_t *c)
261 {
262 filelist_entry_t *entry;
263 mpd_InfoEntity *entity;
265 if (browser->filelist == NULL)
266 return -1;
267 entry = (filelist_entry_t *) g_list_nth_data(browser->filelist->list,
268 browser->lw->selected);
269 if( entry==NULL )
270 return -1;
272 entity = entry->entity;
273 if (entity == NULL || entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY)
274 return browser_change_directory(browser, c, entry, NULL);
275 else if (entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE)
276 return load_playlist(c, entry);
277 else if (entity->type == MPD_INFO_ENTITY_TYPE_SONG)
278 return enqueue_and_play(c, entry);
279 return -1;
280 }
283 #ifdef USE_OLD_ADD
284 /* NOTE - The add_directory functions should move to mpdclient.c */
285 extern gint mpdclient_finish_command(mpdclient_t *c);
287 static int
288 add_directory(mpdclient_t *c, char *dir)
289 {
290 mpd_InfoEntity *entity;
291 GList *subdir_list = NULL;
292 GList *list = NULL;
293 char *dirname;
295 dirname = utf8_to_locale(dir);
296 screen_status_printf(_("Adding directory %s...\n"), dirname);
297 doupdate();
298 g_free(dirname);
299 dirname = NULL;
301 mpd_sendLsInfoCommand(c->connection, dir);
302 mpd_sendCommandListBegin(c->connection);
303 while( (entity=mpd_getNextInfoEntity(c->connection)) ) {
304 if( entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
305 mpd_Song *song = entity->info.song;
306 mpd_sendAddCommand(c->connection, song->file);
307 mpd_freeInfoEntity(entity);
308 } else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) {
309 subdir_list = g_list_append(subdir_list, (gpointer) entity);
310 } else
311 mpd_freeInfoEntity(entity);
312 }
313 mpd_sendCommandListEnd(c->connection);
314 mpdclient_finish_command(c);
315 c->need_update = TRUE;
317 list = g_list_first(subdir_list);
318 while( list!=NULL ) {
319 mpd_Directory *dir;
321 entity = list->data;
322 dir = entity->info.directory;
323 add_directory(c, dir->path);
324 mpd_freeInfoEntity(entity);
325 list->data=NULL;
326 list=list->next;
327 }
328 g_list_free(subdir_list);
329 return 0;
330 }
331 #endif
333 int
334 browser_handle_select(struct screen_browser *browser, mpdclient_t *c)
335 {
336 filelist_entry_t *entry;
338 if (browser->filelist == NULL)
339 return -1;
340 entry = g_list_nth_data(browser->filelist->list, browser->lw->selected);
341 if (entry == NULL || entry->entity == NULL)
342 return -1;
344 if (entry->entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE)
345 return load_playlist(c, entry);
347 if (entry->entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
348 mpd_Directory *dir = entry->entity->info.directory;
349 #ifdef USE_OLD_ADD
350 add_directory(c, tmp);
351 #else
352 if (mpdclient_cmd_add_path_utf8(c, dir->path) == 0) {
353 char *tmp = utf8_to_locale(dir->path);
355 screen_status_printf(_("Adding \'%s\' to playlist\n"), tmp);
356 g_free(tmp);
357 }
358 #endif
359 return 0;
360 }
362 if (entry->entity->type != MPD_INFO_ENTITY_TYPE_SONG)
363 return -1;
365 if (entry->flags & HIGHLIGHT)
366 entry->flags &= ~HIGHLIGHT;
367 else
368 entry->flags |= HIGHLIGHT;
370 if (entry->flags & HIGHLIGHT) {
371 if (entry->entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
372 mpd_Song *song = entry->entity->info.song;
374 if (mpdclient_cmd_add(c, song) == 0) {
375 char buf[BUFSIZE];
377 strfsong(buf, BUFSIZE, LIST_FORMAT, song);
378 screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
379 }
380 }
381 } else {
382 /* remove song from playlist */
383 if (entry->entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
384 mpd_Song *song = entry->entity->info.song;
386 if (song) {
387 int idx;
389 while ((idx = playlist_get_index_from_file(c, song->file)) >=0)
390 mpdclient_cmd_delete(c, idx);
391 }
392 }
393 }
395 return 0;
396 }
398 int
399 browser_handle_select_all(struct screen_browser *browser, mpdclient_t *c)
400 {
401 filelist_entry_t *entry;
402 GList *temp = browser->filelist->list;
404 if (browser->filelist == NULL)
405 return -1;
407 for (browser->filelist->list = g_list_first(browser->filelist->list);
408 browser->filelist->list;
409 browser->filelist->list = g_list_next(browser->filelist->list)) {
410 entry = browser->filelist->list->data;
411 if (entry == NULL || entry->entity == NULL)
412 return -1;
414 if (entry->entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE)
415 load_playlist(c, entry);
417 if (entry->entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
418 mpd_Directory *dir = entry->entity->info.directory;
419 #ifdef USE_OLD_ADD
420 add_directory(c, tmp);
421 #else
422 if (mpdclient_cmd_add_path_utf8(c, dir->path) == 0) {
423 char *tmp = utf8_to_locale(dir->path);
425 screen_status_printf(_("Adding \'%s\' to playlist\n"), tmp);
426 g_free(tmp);
427 }
428 #endif
429 }
431 if (entry->entity->type != MPD_INFO_ENTITY_TYPE_SONG)
432 continue;
434 entry->flags |= HIGHLIGHT;
436 if (entry->flags & HIGHLIGHT) {
437 if (entry->entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
438 mpd_Song *song = entry->entity->info.song;
440 if (mpdclient_cmd_add(c, song) == 0) {
441 char buf[BUFSIZE];
443 strfsong(buf, BUFSIZE, LIST_FORMAT, song);
444 screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
445 }
446 }
447 }
448 return 0;
449 }
451 browser->filelist->list = temp;
452 return 0;
453 }
455 #ifdef HAVE_GETMOUSE
456 int
457 browser_handle_mouse_event(struct screen_browser *browser, mpdclient_t *c)
458 {
459 int row;
460 unsigned prev_selected = browser->lw->selected;
461 unsigned long bstate;
462 int length;
464 if (browser->filelist)
465 length = browser->filelist->length;
466 else
467 length = 0;
469 if( screen_get_mouse_event(c, browser->lw, length, &bstate, &row) )
470 return 1;
472 browser->lw->selected = browser->lw->start + row;
473 list_window_check_selected(browser->lw, length);
475 if( bstate & BUTTON1_CLICKED ) {
476 if (prev_selected == browser->lw->selected)
477 browser_handle_enter(browser, c);
478 } else if (bstate & BUTTON3_CLICKED) {
479 if (prev_selected == browser->lw->selected)
480 browser_handle_select(browser, c);
481 }
483 return 1;
484 }
485 #endif