1 /*
2 * (c) 2004 by Kalle Wallin <kaw@linux.se>
3 * Copyright (C) 2008 Max Kellermann <max@duempel.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 */
20 #include "screen_browser.h"
21 #include "i18n.h"
22 #include "options.h"
23 #include "charset.h"
24 #include "support.h"
25 #include "strfsong.h"
26 #include "screen_utils.h"
27 #include "gcc.h"
29 #include <string.h>
31 #define USE_OLD_LAYOUT
32 #undef USE_OLD_ADD
34 #define BUFSIZE 1024
36 #define HIGHLIGHT (0x01)
38 /* clear the highlight flag for all items in the filelist */
39 static void
40 clear_highlights(mpdclient_filelist_t *fl)
41 {
42 guint i;
44 for (i = 0; i < filelist_length(fl); ++i) {
45 struct filelist_entry *entry = filelist_get(fl, i);
47 entry->flags &= ~HIGHLIGHT;
48 }
49 }
51 /* change the highlight flag for a song */
52 static void
53 set_highlight(mpdclient_filelist_t *fl, mpd_Song *song, int highlight)
54 {
55 struct filelist_entry *entry = filelist_find_song(fl, song);
56 mpd_InfoEntity *entity;
58 if (entry == NULL)
59 return;
61 entity = entry->entity;
62 if (highlight)
63 entry->flags |= HIGHLIGHT;
64 else
65 entry->flags &= ~HIGHLIGHT;
66 }
68 /* sync highlight flags with playlist */
69 void
70 sync_highlights(mpdclient_t *c, mpdclient_filelist_t *fl)
71 {
72 guint i;
74 for (i = 0; i < filelist_length(fl); ++i) {
75 struct filelist_entry *entry = filelist_get(fl, i);
76 mpd_InfoEntity *entity = entry->entity;
78 if ( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
79 mpd_Song *song = entity->info.song;
81 if( playlist_get_index_from_file(c, song->file) >= 0 )
82 entry->flags |= HIGHLIGHT;
83 else
84 entry->flags &= ~HIGHLIGHT;
85 }
86 }
87 }
89 /* the playlist have been updated -> fix highlights */
90 void
91 browser_playlist_changed(struct screen_browser *browser, mpdclient_t *c,
92 int event, gpointer data)
93 {
94 if (browser->filelist == NULL)
95 return;
97 switch(event) {
98 case PLAYLIST_EVENT_CLEAR:
99 clear_highlights(browser->filelist);
100 break;
101 case PLAYLIST_EVENT_ADD:
102 set_highlight(browser->filelist, (mpd_Song *) data, 1);
103 break;
104 case PLAYLIST_EVENT_DELETE:
105 set_highlight(browser->filelist, (mpd_Song *) data, 0);
106 break;
107 case PLAYLIST_EVENT_MOVE:
108 break;
109 default:
110 sync_highlights(c, browser->filelist);
111 break;
112 }
113 }
115 /* list_window callback */
116 const char *
117 browser_lw_callback(unsigned idx, int *highlight, void *data)
118 {
119 static char buf[BUFSIZE];
120 mpdclient_filelist_t *fl = (mpdclient_filelist_t *) data;
121 filelist_entry_t *entry;
122 mpd_InfoEntity *entity;
124 if (idx >= filelist_length(fl))
125 return NULL;
127 entry = filelist_get(fl, idx);
128 assert(entry != NULL);
130 entity = entry->entity;
131 *highlight = (entry->flags & HIGHLIGHT);
133 if( entity == NULL )
134 return "[..]";
136 if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) {
137 mpd_Directory *dir = entity->info.directory;
138 char *directory = utf8_to_locale(basename(dir->path));
140 g_snprintf(buf, BUFSIZE, "[%s]", directory);
141 g_free(directory);
142 return buf;
143 } else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
144 mpd_Song *song = entity->info.song;
146 strfsong(buf, BUFSIZE, options.list_format, song);
147 return buf;
148 } else if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE ) {
149 mpd_PlaylistFile *plf = entity->info.playlistFile;
150 char *filename = utf8_to_locale(basename(plf->path));
152 #ifdef USE_OLD_LAYOUT
153 g_snprintf(buf, BUFSIZE, "*%s*", filename);
154 #else
155 g_snprintf(buf, BUFSIZE, "<Playlist> %s", filename);
156 #endif
157 g_free(filename);
158 return buf;
159 }
161 return "Error: Unknown entry!";
162 }
164 /* chdir */
165 int
166 browser_change_directory(struct screen_browser *browser, mpdclient_t *c,
167 filelist_entry_t *entry, const char *new_path)
168 {
169 mpd_InfoEntity *entity = NULL;
170 gchar *path = NULL;
172 if( entry!=NULL )
173 entity = entry->entity;
174 else if( new_path==NULL )
175 return -1;
177 if( entity==NULL ) {
178 if( entry || 0==strcmp(new_path, "..") ) {
179 /* return to parent */
180 char *parent = g_path_get_dirname(browser->filelist->path);
181 if( strcmp(parent, ".") == 0 )
182 parent[0] = '\0';
183 path = g_strdup(parent);
184 list_window_reset(browser->lw);
185 /* restore previous list window state */
186 list_window_pop_state(browser->lw_state, browser->lw);
187 } else {
188 /* entry==NULL, then new_path ("" is root) */
189 path = g_strdup(new_path);
190 list_window_reset(browser->lw);
191 /* restore first list window state (pop while returning true) */
192 while(list_window_pop_state(browser->lw_state, browser->lw));
193 }
194 } else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY) {
195 /* enter sub */
196 mpd_Directory *dir = entity->info.directory;
197 path = utf8_to_locale(dir->path);
198 /* save current list window state */
199 list_window_push_state(browser->lw_state, browser->lw);
200 } else
201 return -1;
203 filelist_free(browser->filelist);
204 browser->filelist = mpdclient_filelist_get(c, path);
205 sync_highlights(c, browser->filelist);
206 list_window_check_selected(browser->lw, filelist_length(browser->filelist));
207 g_free(path);
208 return 0;
209 }
211 static int
212 load_playlist(mpdclient_t *c, filelist_entry_t *entry)
213 {
214 mpd_InfoEntity *entity = entry->entity;
215 mpd_PlaylistFile *plf = entity->info.playlistFile;
216 char *filename = utf8_to_locale(plf->path);
218 if (mpdclient_cmd_load_playlist_utf8(c, plf->path) == 0)
219 screen_status_printf(_("Loading playlist %s..."), basename(filename));
220 g_free(filename);
221 return 0;
222 }
224 static int
225 enqueue_and_play(mpdclient_t *c, filelist_entry_t *entry)
226 {
227 int idx;
228 mpd_InfoEntity *entity = entry->entity;
229 mpd_Song *song = entity->info.song;
231 if (!(entry->flags & HIGHLIGHT)) {
232 if (mpdclient_cmd_add(c, song) == 0) {
233 char buf[BUFSIZE];
235 entry->flags |= HIGHLIGHT;
236 strfsong(buf, BUFSIZE, options.list_format, song);
237 screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
238 mpdclient_update(c); /* get song id */
239 } else
240 return -1;
241 }
243 idx = playlist_get_index_from_file(c, song->file);
244 mpdclient_cmd_play(c, idx);
245 return 0;
246 }
248 static struct filelist_entry *
249 browser_get_selected(const struct screen_browser *browser)
250 {
251 if (browser->filelist == NULL ||
252 browser->lw->selected >= filelist_length(browser->filelist))
253 return NULL;
255 return filelist_get(browser->filelist, browser->lw->selected);
256 }
258 static int
259 browser_handle_enter(struct screen_browser *browser, mpdclient_t *c)
260 {
261 struct filelist_entry *entry = browser_get_selected(browser);
262 mpd_InfoEntity *entity;
264 if( entry==NULL )
265 return -1;
267 entity = entry->entity;
268 if (entity == NULL || entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY)
269 return browser_change_directory(browser, c, entry, NULL);
270 else if (entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE)
271 return load_playlist(c, entry);
272 else if (entity->type == MPD_INFO_ENTITY_TYPE_SONG)
273 return enqueue_and_play(c, entry);
274 return -1;
275 }
278 #ifdef USE_OLD_ADD
279 /* NOTE - The add_directory functions should move to mpdclient.c */
280 extern gint mpdclient_finish_command(mpdclient_t *c);
282 static int
283 add_directory(mpdclient_t *c, char *dir)
284 {
285 mpd_InfoEntity *entity;
286 GList *subdir_list = NULL;
287 GList *list = NULL;
288 char *dirname;
290 dirname = utf8_to_locale(dir);
291 screen_status_printf(_("Adding directory %s...\n"), dirname);
292 doupdate();
293 g_free(dirname);
294 dirname = NULL;
296 mpd_sendLsInfoCommand(c->connection, dir);
297 mpd_sendCommandListBegin(c->connection);
298 while( (entity=mpd_getNextInfoEntity(c->connection)) ) {
299 if( entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
300 mpd_Song *song = entity->info.song;
301 mpd_sendAddCommand(c->connection, song->file);
302 mpd_freeInfoEntity(entity);
303 } else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) {
304 subdir_list = g_list_append(subdir_list, (gpointer) entity);
305 } else
306 mpd_freeInfoEntity(entity);
307 }
308 mpd_sendCommandListEnd(c->connection);
309 mpdclient_finish_command(c);
310 c->need_update = TRUE;
312 list = g_list_first(subdir_list);
313 while( list!=NULL ) {
314 mpd_Directory *dir;
316 entity = list->data;
317 dir = entity->info.directory;
318 add_directory(c, dir->path);
319 mpd_freeInfoEntity(entity);
320 list->data=NULL;
321 list=list->next;
322 }
323 g_list_free(subdir_list);
324 return 0;
325 }
326 #endif
328 static int
329 browser_select_entry(mpdclient_t *c, filelist_entry_t *entry,
330 gboolean toggle)
331 {
332 assert(entry != NULL);
333 assert(entry->entity != NULL);
335 if (entry->entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE)
336 return load_playlist(c, entry);
338 if (entry->entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
339 mpd_Directory *dir = entry->entity->info.directory;
340 #ifdef USE_OLD_ADD
341 add_directory(c, tmp);
342 #else
343 if (mpdclient_cmd_add_path_utf8(c, dir->path) == 0) {
344 char *tmp = utf8_to_locale(dir->path);
346 screen_status_printf(_("Adding \'%s\' to playlist\n"), tmp);
347 g_free(tmp);
348 }
349 #endif
350 return 0;
351 }
353 if (entry->entity->type != MPD_INFO_ENTITY_TYPE_SONG)
354 return -1;
356 assert(entry->entity->info.song != NULL);
358 if (!toggle || (entry->flags & HIGHLIGHT) == 0) {
359 mpd_Song *song = entry->entity->info.song;
361 entry->flags |= HIGHLIGHT;
363 if (mpdclient_cmd_add(c, song) == 0) {
364 char buf[BUFSIZE];
366 strfsong(buf, BUFSIZE, options.list_format, song);
367 screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
368 }
369 } else {
370 /* remove song from playlist */
371 mpd_Song *song = entry->entity->info.song;
372 int idx;
374 entry->flags &= ~HIGHLIGHT;
376 while ((idx = playlist_get_index_from_file(c, song->file)) >=0)
377 mpdclient_cmd_delete(c, idx);
378 }
380 return 0;
381 }
383 static int
384 browser_handle_select(struct screen_browser *browser, mpdclient_t *c)
385 {
386 struct filelist_entry *entry = browser_get_selected(browser);
388 if (entry == NULL || entry->entity == NULL)
389 return -1;
391 return browser_select_entry(c, entry, TRUE);
392 }
394 static int
395 browser_handle_add(struct screen_browser *browser, mpdclient_t *c)
396 {
397 struct filelist_entry *entry = browser_get_selected(browser);
399 if (entry == NULL || entry->entity == NULL)
400 return -1;
402 return browser_select_entry(c, entry, FALSE);
403 }
405 static void
406 browser_handle_select_all(struct screen_browser *browser, mpdclient_t *c)
407 {
408 guint i;
410 if (browser->filelist == NULL)
411 return;
413 for (i = 0; i < filelist_length(browser->filelist); ++i) {
414 struct filelist_entry *entry = filelist_get(browser->filelist, i);
416 if (entry != NULL && entry->entity != NULL)
417 browser_select_entry(c, entry, FALSE);
418 }
419 }
421 #ifdef HAVE_GETMOUSE
422 static int
423 browser_handle_mouse_event(struct screen_browser *browser, mpdclient_t *c)
424 {
425 int row;
426 unsigned prev_selected = browser->lw->selected;
427 unsigned long bstate;
428 int length;
430 if (browser->filelist)
431 length = filelist_length(browser->filelist);
432 else
433 length = 0;
435 if (screen_get_mouse_event(c, &bstate, &row) ||
436 list_window_mouse(browser->lw, length, bstate, row))
437 return 1;
439 browser->lw->selected = browser->lw->start + row;
440 list_window_check_selected(browser->lw, length);
442 if( bstate & BUTTON1_CLICKED ) {
443 if (prev_selected == browser->lw->selected)
444 browser_handle_enter(browser, c);
445 } else if (bstate & BUTTON3_CLICKED) {
446 if (prev_selected == browser->lw->selected)
447 browser_handle_select(browser, c);
448 }
450 return 1;
451 }
452 #endif
454 bool
455 browser_cmd(struct screen_browser *browser, struct screen *screen,
456 struct mpdclient *c, command_t cmd)
457 {
458 struct filelist_entry *entry;
460 switch (cmd) {
461 case CMD_PLAY:
462 browser_handle_enter(browser, c);
463 return true;
465 case CMD_SELECT:
466 if (browser_handle_select(browser, c) == 0)
467 /* continue and select next item... */
468 cmd = CMD_LIST_NEXT;
470 /* call list_window_cmd to go to the next item */
471 break;
473 case CMD_ADD:
474 if (browser_handle_add(browser, c) == 0)
475 /* continue and select next item... */
476 cmd = CMD_LIST_NEXT;
478 /* call list_window_cmd to go to the next item */
479 break;
481 case CMD_SELECT_ALL:
482 browser_handle_select_all(browser, c);
483 return true;
485 case CMD_LIST_FIND:
486 case CMD_LIST_RFIND:
487 case CMD_LIST_FIND_NEXT:
488 case CMD_LIST_RFIND_NEXT:
489 screen_find(screen,
490 browser->lw, filelist_length(browser->filelist),
491 cmd, browser_lw_callback,
492 browser->filelist);
493 return true;
495 #ifdef HAVE_GETMOUSE
496 case CMD_MOUSE_EVENT:
497 browser_handle_mouse_event(browser, c);
498 return true;
499 #endif
501 #ifdef ENABLE_LYRICS_SCREEN
502 case CMD_SCREEN_LYRICS:
503 entry = browser_get_selected(browser);
504 if (entry == NULL)
505 return false;
507 if (entry->entity == NULL ||
508 entry->entity->type != MPD_INFO_ENTITY_TYPE_SONG)
509 return true;
511 screen_lyrics_switch(c, entry->entity->info.song);
512 return true;
513 #endif
515 default:
516 break;
517 }
519 if (list_window_cmd(browser->lw, filelist_length(browser->filelist),
520 cmd))
521 return true;
523 return false;
524 }