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 "strfsong.h"
25 #include "screen_utils.h"
26 #include "gcc.h"
28 #include <string.h>
30 #undef USE_OLD_ADD
32 #define BUFSIZE 1024
34 #ifndef NCMPC_MINI
35 #define HIGHLIGHT (0x01)
36 #endif
38 static const char playlist_format[] = "*%s*";
40 #ifndef NCMPC_MINI
42 /* clear the highlight flag for all items in the filelist */
43 static void
44 clear_highlights(mpdclient_filelist_t *fl)
45 {
46 guint i;
48 for (i = 0; i < filelist_length(fl); ++i) {
49 struct filelist_entry *entry = filelist_get(fl, i);
51 entry->flags &= ~HIGHLIGHT;
52 }
53 }
55 /* change the highlight flag for a song */
56 static void
57 set_highlight(mpdclient_filelist_t *fl, mpd_Song *song, int highlight)
58 {
59 struct filelist_entry *entry = filelist_find_song(fl, song);
60 mpd_InfoEntity *entity;
62 if (entry == NULL)
63 return;
65 entity = entry->entity;
66 if (highlight)
67 entry->flags |= HIGHLIGHT;
68 else
69 entry->flags &= ~HIGHLIGHT;
70 }
72 /* sync highlight flags with playlist */
73 void
74 sync_highlights(mpdclient_t *c, mpdclient_filelist_t *fl)
75 {
76 guint i;
78 for (i = 0; i < filelist_length(fl); ++i) {
79 struct filelist_entry *entry = filelist_get(fl, i);
80 mpd_InfoEntity *entity = entry->entity;
82 if ( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
83 mpd_Song *song = entity->info.song;
85 if( playlist_get_index_from_file(c, song->file) >= 0 )
86 entry->flags |= HIGHLIGHT;
87 else
88 entry->flags &= ~HIGHLIGHT;
89 }
90 }
91 }
93 /* the playlist have been updated -> fix highlights */
94 void
95 browser_playlist_changed(struct screen_browser *browser, mpdclient_t *c,
96 int event, gpointer data)
97 {
98 if (browser->filelist == NULL)
99 return;
101 switch(event) {
102 case PLAYLIST_EVENT_CLEAR:
103 clear_highlights(browser->filelist);
104 break;
105 case PLAYLIST_EVENT_ADD:
106 set_highlight(browser->filelist, (mpd_Song *) data, 1);
107 break;
108 case PLAYLIST_EVENT_DELETE:
109 set_highlight(browser->filelist, (mpd_Song *) data, 0);
110 break;
111 case PLAYLIST_EVENT_MOVE:
112 break;
113 default:
114 sync_highlights(c, browser->filelist);
115 break;
116 }
117 }
119 #endif
121 /* list_window callback */
122 const char *
123 browser_lw_callback(unsigned idx, int *highlight, void *data)
124 {
125 static char buf[BUFSIZE];
126 mpdclient_filelist_t *fl = (mpdclient_filelist_t *) data;
127 filelist_entry_t *entry;
128 mpd_InfoEntity *entity;
130 if (idx >= filelist_length(fl))
131 return NULL;
133 entry = filelist_get(fl, idx);
134 assert(entry != NULL);
136 entity = entry->entity;
137 #ifndef NCMPC_MINI
138 *highlight = (entry->flags & HIGHLIGHT);
139 #else
140 *highlight = false;
141 #endif
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(g_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, options.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(g_basename(plf->path));
162 g_snprintf(buf, BUFSIZE, playlist_format, filename);
163 g_free(filename);
164 return buf;
165 }
167 return "Error: Unknown entry!";
168 }
170 /* chdir */
171 bool
172 browser_change_directory(struct screen_browser *browser, mpdclient_t *c,
173 filelist_entry_t *entry, const char *new_path)
174 {
175 mpd_InfoEntity *entity = NULL;
176 gchar *path = NULL;
177 char *old_path;
178 int idx;
180 if( entry!=NULL )
181 entity = entry->entity;
182 else if( new_path==NULL )
183 return false;
185 if( entity==NULL ) {
186 if( entry || 0==strcmp(new_path, "..") ) {
187 /* return to parent */
188 char *parent = g_path_get_dirname(browser->filelist->path);
189 if( strcmp(parent, ".") == 0 )
190 parent[0] = '\0';
191 path = g_strdup(parent);
192 g_free(parent);
193 } else {
194 /* entry==NULL, then new_path ("" is root) */
195 path = g_strdup(new_path);
196 }
197 } else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY) {
198 /* enter sub */
199 mpd_Directory *dir = entity->info.directory;
200 path = g_strdup(dir->path);
201 } else
202 return false;
204 old_path = g_strdup(browser->filelist->path);
206 filelist_free(browser->filelist);
207 browser->filelist = mpdclient_filelist_get(c, path);
208 #ifndef NCMPC_MINI
209 sync_highlights(c, browser->filelist);
210 #endif
212 idx = filelist_find_directory(browser->filelist, old_path);
213 g_free(old_path);
215 list_window_reset(browser->lw);
216 if (idx >= 0) {
217 list_window_set_selected(browser->lw, idx);
218 list_window_center(browser->lw,
219 filelist_length(browser->filelist), idx);
220 }
222 g_free(path);
223 return true;
224 }
226 static bool
227 load_playlist(mpdclient_t *c, filelist_entry_t *entry)
228 {
229 mpd_InfoEntity *entity = entry->entity;
230 mpd_PlaylistFile *plf = entity->info.playlistFile;
231 char *filename = utf8_to_locale(plf->path);
233 if (mpdclient_cmd_load_playlist(c, plf->path) == 0)
234 screen_status_printf(_("Loading playlist %s..."),
235 g_basename(filename));
236 g_free(filename);
237 return true;
238 }
240 static bool
241 enqueue_and_play(mpdclient_t *c, filelist_entry_t *entry)
242 {
243 int idx;
244 mpd_InfoEntity *entity = entry->entity;
245 mpd_Song *song = entity->info.song;
247 #ifndef NCMPC_MINI
248 if (!(entry->flags & HIGHLIGHT)) {
249 #endif
250 if (mpdclient_cmd_add(c, song) == 0) {
251 char buf[BUFSIZE];
253 #ifndef NCMPC_MINI
254 entry->flags |= HIGHLIGHT;
255 #endif
256 strfsong(buf, BUFSIZE, options.list_format, song);
257 screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
258 mpdclient_update(c); /* get song id */
259 } else
260 return false;
261 #ifndef NCMPC_MINI
262 }
263 #endif
265 idx = playlist_get_index_from_file(c, song->file);
266 mpdclient_cmd_play(c, idx);
267 return true;
268 }
270 static struct filelist_entry *
271 browser_get_selected(const struct screen_browser *browser)
272 {
273 if (browser->filelist == NULL ||
274 browser->lw->selected >= filelist_length(browser->filelist))
275 return NULL;
277 return filelist_get(browser->filelist, browser->lw->selected);
278 }
280 static bool
281 browser_handle_enter(struct screen_browser *browser, mpdclient_t *c)
282 {
283 struct filelist_entry *entry = browser_get_selected(browser);
284 mpd_InfoEntity *entity;
286 if( entry==NULL )
287 return false;
289 entity = entry->entity;
290 if (entity == NULL || entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY)
291 return browser_change_directory(browser, c, entry, NULL);
292 else if (entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE)
293 return load_playlist(c, entry);
294 else if (entity->type == MPD_INFO_ENTITY_TYPE_SONG)
295 return enqueue_and_play(c, entry);
296 return false;
297 }
300 #ifdef USE_OLD_ADD
302 static int
303 add_directory(mpdclient_t *c, char *dir)
304 {
305 mpd_InfoEntity *entity;
306 GList *subdir_list = NULL;
307 GList *list = NULL;
308 char *dirname;
310 dirname = utf8_to_locale(dir);
311 screen_status_printf(_("Adding directory %s...\n"), dirname);
312 doupdate();
313 g_free(dirname);
314 dirname = NULL;
316 mpd_sendLsInfoCommand(c->connection, dir);
317 mpd_sendCommandListBegin(c->connection);
318 while( (entity=mpd_getNextInfoEntity(c->connection)) ) {
319 if( entity->type==MPD_INFO_ENTITY_TYPE_SONG ) {
320 mpd_Song *song = entity->info.song;
321 mpd_sendAddCommand(c->connection, song->file);
322 mpd_freeInfoEntity(entity);
323 } else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) {
324 subdir_list = g_list_append(subdir_list, (gpointer) entity);
325 } else
326 mpd_freeInfoEntity(entity);
327 }
328 mpd_sendCommandListEnd(c->connection);
329 mpdclient_finish_command(c);
330 c->need_update = TRUE;
332 list = g_list_first(subdir_list);
333 while( list!=NULL ) {
334 mpd_Directory *dir;
336 entity = list->data;
337 dir = entity->info.directory;
338 add_directory(c, dir->path);
339 mpd_freeInfoEntity(entity);
340 list->data=NULL;
341 list=list->next;
342 }
343 g_list_free(subdir_list);
344 return 0;
345 }
346 #endif
348 static bool
349 browser_select_entry(mpdclient_t *c, filelist_entry_t *entry,
350 mpd_unused gboolean toggle)
351 {
352 assert(entry != NULL);
353 assert(entry->entity != NULL);
355 if (entry->entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE)
356 return load_playlist(c, entry);
358 if (entry->entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
359 mpd_Directory *dir = entry->entity->info.directory;
360 #ifdef USE_OLD_ADD
361 add_directory(c, tmp);
362 #else
363 if (mpdclient_cmd_add_path(c, dir->path) == 0) {
364 char *tmp = utf8_to_locale(dir->path);
366 screen_status_printf(_("Adding \'%s\' to playlist\n"), tmp);
367 g_free(tmp);
368 }
369 #endif
370 return true;
371 }
373 if (entry->entity->type != MPD_INFO_ENTITY_TYPE_SONG)
374 return false;
376 assert(entry->entity->info.song != NULL);
378 #ifndef NCMPC_MINI
379 if (!toggle || (entry->flags & HIGHLIGHT) == 0)
380 #endif
381 {
382 mpd_Song *song = entry->entity->info.song;
384 #ifndef NCMPC_MINI
385 entry->flags |= HIGHLIGHT;
386 #endif
388 if (mpdclient_cmd_add(c, song) == 0) {
389 char buf[BUFSIZE];
391 strfsong(buf, BUFSIZE, options.list_format, song);
392 screen_status_printf(_("Adding \'%s\' to playlist\n"), buf);
393 }
394 #ifndef NCMPC_MINI
395 } else {
396 /* remove song from playlist */
397 mpd_Song *song = entry->entity->info.song;
398 int idx;
400 entry->flags &= ~HIGHLIGHT;
402 while ((idx = playlist_get_index_from_file(c, song->file)) >=0)
403 mpdclient_cmd_delete(c, idx);
404 #endif
405 }
407 return true;
408 }
410 static bool
411 browser_handle_select(struct screen_browser *browser, mpdclient_t *c)
412 {
413 struct filelist_entry *entry = browser_get_selected(browser);
415 if (entry == NULL || entry->entity == NULL)
416 return false;
418 return browser_select_entry(c, entry, TRUE);
419 }
421 static bool
422 browser_handle_add(struct screen_browser *browser, mpdclient_t *c)
423 {
424 struct filelist_entry *entry = browser_get_selected(browser);
426 if (entry == NULL || entry->entity == NULL)
427 return false;
429 return browser_select_entry(c, entry, FALSE);
430 }
432 static void
433 browser_handle_select_all(struct screen_browser *browser, mpdclient_t *c)
434 {
435 guint i;
437 if (browser->filelist == NULL)
438 return;
440 for (i = 0; i < filelist_length(browser->filelist); ++i) {
441 struct filelist_entry *entry = filelist_get(browser->filelist, i);
443 if (entry != NULL && entry->entity != NULL)
444 browser_select_entry(c, entry, FALSE);
445 }
446 }
448 #ifdef HAVE_GETMOUSE
449 static int
450 browser_handle_mouse_event(struct screen_browser *browser, mpdclient_t *c)
451 {
452 int row;
453 unsigned prev_selected = browser->lw->selected;
454 unsigned long bstate;
455 int length;
457 if (browser->filelist)
458 length = filelist_length(browser->filelist);
459 else
460 length = 0;
462 if (screen_get_mouse_event(c, &bstate, &row) ||
463 list_window_mouse(browser->lw, length, bstate, row))
464 return 1;
466 browser->lw->selected = browser->lw->start + row;
467 list_window_check_selected(browser->lw, length);
469 if( bstate & BUTTON1_CLICKED ) {
470 if (prev_selected == browser->lw->selected)
471 browser_handle_enter(browser, c);
472 } else if (bstate & BUTTON3_CLICKED) {
473 if (prev_selected == browser->lw->selected)
474 browser_handle_select(browser, c);
475 }
477 return 1;
478 }
479 #endif
481 bool
482 browser_cmd(struct screen_browser *browser,
483 struct mpdclient *c, command_t cmd)
484 {
485 #ifdef ENABLE_LYRICS_SCREEN
486 struct filelist_entry *entry;
487 #endif
489 switch (cmd) {
490 case CMD_PLAY:
491 browser_handle_enter(browser, c);
492 return true;
494 case CMD_SELECT:
495 if (browser_handle_select(browser, c))
496 /* continue and select next item... */
497 cmd = CMD_LIST_NEXT;
499 /* call list_window_cmd to go to the next item */
500 break;
502 case CMD_ADD:
503 if (browser_handle_add(browser, c))
504 /* continue and select next item... */
505 cmd = CMD_LIST_NEXT;
507 /* call list_window_cmd to go to the next item */
508 break;
510 case CMD_SELECT_ALL:
511 browser_handle_select_all(browser, c);
512 return true;
514 case CMD_LIST_FIND:
515 case CMD_LIST_RFIND:
516 case CMD_LIST_FIND_NEXT:
517 case CMD_LIST_RFIND_NEXT:
518 screen_find(browser->lw, filelist_length(browser->filelist),
519 cmd, browser_lw_callback,
520 browser->filelist);
521 return true;
523 #ifdef HAVE_GETMOUSE
524 case CMD_MOUSE_EVENT:
525 browser_handle_mouse_event(browser, c);
526 return true;
527 #endif
529 #ifdef ENABLE_LYRICS_SCREEN
530 case CMD_SCREEN_LYRICS:
531 entry = browser_get_selected(browser);
532 if (entry == NULL)
533 return false;
535 if (entry->entity == NULL ||
536 entry->entity->type != MPD_INFO_ENTITY_TYPE_SONG)
537 return true;
539 screen_lyrics_switch(c, entry->entity->info.song);
540 return true;
541 #endif
543 default:
544 break;
545 }
547 if (list_window_cmd(browser->lw, filelist_length(browser->filelist),
548 cmd))
549 return true;
551 return false;
552 }