efa2a387de144d43df5f94e1681a75cb51005ec5
1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2009 The Music Player Daemon Project
3 * Project homepage: http://musicpd.org
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.
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.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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 "mpdclient.h"
27 #include "filelist.h"
29 #include <mpd/client.h>
31 #include <string.h>
33 #define BUFSIZE 1024
35 #ifndef NCMPC_MINI
36 #define HIGHLIGHT (0x01)
37 #endif
39 static const char playlist_format[] = "*%s*";
41 #ifndef NCMPC_MINI
43 /* clear the highlight flag for all items in the filelist */
44 static void
45 clear_highlights(struct filelist *fl)
46 {
47 guint i;
49 for (i = 0; i < filelist_length(fl); ++i) {
50 struct filelist_entry *entry = filelist_get(fl, i);
52 entry->flags &= ~HIGHLIGHT;
53 }
54 }
56 /* change the highlight flag for a song */
57 static void
58 set_highlight(struct filelist *fl, struct mpd_song *song, int highlight)
59 {
60 int i = filelist_find_song(fl, song);
61 struct filelist_entry *entry;
63 if (i < 0)
64 return;
66 entry = filelist_get(fl, i);
67 if (highlight)
68 entry->flags |= HIGHLIGHT;
69 else
70 entry->flags &= ~HIGHLIGHT;
71 }
73 /* sync highlight flags with playlist */
74 void
75 sync_highlights(struct mpdclient *c, struct filelist *fl)
76 {
77 guint i;
79 for (i = 0; i < filelist_length(fl); ++i) {
80 struct filelist_entry *entry = filelist_get(fl, i);
81 struct mpd_entity *entity = entry->entity;
83 if (entity != NULL && mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
84 const struct mpd_song *song =
85 mpd_entity_get_song(entity);
87 if (playlist_get_index_from_same_song(c, song) >= 0)
88 entry->flags |= HIGHLIGHT;
89 else
90 entry->flags &= ~HIGHLIGHT;
91 }
92 }
93 }
95 /* the playlist has been updated -> fix highlights */
96 void
97 browser_playlist_changed(struct screen_browser *browser, struct mpdclient *c,
98 int event, gpointer data)
99 {
100 if (browser->filelist == NULL)
101 return;
103 switch(event) {
104 case PLAYLIST_EVENT_CLEAR:
105 clear_highlights(browser->filelist);
106 break;
107 case PLAYLIST_EVENT_ADD:
108 set_highlight(browser->filelist, (struct mpd_song *) data, 1);
109 break;
110 case PLAYLIST_EVENT_DELETE:
111 set_highlight(browser->filelist, (struct mpd_song *) data, 0);
112 break;
113 case PLAYLIST_EVENT_MOVE:
114 break;
115 default:
116 sync_highlights(c, browser->filelist);
117 break;
118 }
119 }
121 #endif
123 /* list_window callback */
124 const char *
125 browser_lw_callback(unsigned idx, bool *highlight, G_GNUC_UNUSED char **second_column, void *data)
126 {
127 struct filelist *fl = (struct filelist *) data;
128 static char buf[BUFSIZE];
129 struct filelist_entry *entry;
130 struct mpd_entity *entity;
132 if (fl == NULL || idx >= filelist_length(fl))
133 return NULL;
135 entry = filelist_get(fl, idx);
136 assert(entry != NULL);
138 entity = entry->entity;
139 #ifndef NCMPC_MINI
140 *highlight = (entry->flags & HIGHLIGHT) != 0;
141 #else
142 *highlight = false;
143 #endif
145 if( entity == NULL )
146 return "[..]";
148 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_DIRECTORY) {
149 const struct mpd_directory *dir =
150 mpd_entity_get_directory(entity);
151 char *directory = utf8_to_locale(g_basename(mpd_directory_get_path(dir)));
153 g_snprintf(buf, BUFSIZE, "[%s]", directory);
154 g_free(directory);
155 return buf;
156 } else if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
157 const struct mpd_song *song = mpd_entity_get_song(entity);
159 strfsong(buf, BUFSIZE, options.list_format, song);
160 return buf;
161 } else if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_PLAYLIST) {
162 const struct mpd_playlist *playlist =
163 mpd_entity_get_playlist(entity);
164 char *filename = utf8_to_locale(g_basename(mpd_playlist_get_path(playlist)));
166 g_snprintf(buf, BUFSIZE, playlist_format, filename);
167 g_free(filename);
168 return buf;
169 }
171 return "Error: Unknown entry!";
172 }
174 static bool
175 load_playlist(struct mpdclient *c, const struct mpd_playlist *playlist)
176 {
177 char *filename = utf8_to_locale(mpd_playlist_get_path(playlist));
179 if (mpdclient_cmd_load_playlist(c, mpd_playlist_get_path(playlist)) == 0)
180 screen_status_printf(_("Loading playlist %s..."),
181 g_basename(filename));
182 g_free(filename);
183 return true;
184 }
186 static bool
187 enqueue_and_play(struct mpdclient *c, struct filelist_entry *entry)
188 {
189 int idx;
190 const struct mpd_song *song = mpd_entity_get_song(entry->entity);
192 #ifndef NCMPC_MINI
193 if (!(entry->flags & HIGHLIGHT)) {
194 #endif
195 if (mpdclient_cmd_add(c, song) == 0) {
196 char buf[BUFSIZE];
198 #ifndef NCMPC_MINI
199 entry->flags |= HIGHLIGHT;
200 #endif
201 strfsong(buf, BUFSIZE, options.list_format, song);
202 screen_status_printf(_("Adding \'%s\' to playlist"), buf);
203 mpdclient_update(c); /* get song id */
204 } else
205 return false;
206 #ifndef NCMPC_MINI
207 }
208 #endif
210 idx = playlist_get_index_from_same_song(c, song);
211 mpdclient_cmd_play(c, idx);
212 return true;
213 }
215 struct filelist_entry *
216 browser_get_selected_entry(const struct screen_browser *browser)
217 {
218 if (browser->filelist == NULL ||
219 browser->lw->selected_start < browser->lw->selected_end ||
220 browser->lw->selected >= filelist_length(browser->filelist))
221 return NULL;
223 return filelist_get(browser->filelist, browser->lw->selected);
224 }
226 static const struct mpd_entity *
227 browser_get_selected_entity(const struct screen_browser *browser)
228 {
229 const struct filelist_entry *entry = browser_get_selected_entry(browser);
231 return entry != NULL
232 ? entry->entity
233 : NULL;
234 }
236 static const struct mpd_song *
237 browser_get_selected_song(const struct screen_browser *browser)
238 {
239 const struct mpd_entity *entity = browser_get_selected_entity(browser);
241 return entity != NULL &&
242 mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG
243 ? mpd_entity_get_song(entity)
244 : NULL;
245 }
247 static struct filelist_entry *
248 browser_get_index(const struct screen_browser *browser, unsigned i)
249 {
250 if (browser->filelist == NULL ||
251 i >= filelist_length(browser->filelist))
252 return NULL;
254 return filelist_get(browser->filelist, i);
255 }
257 static bool
258 browser_handle_enter(struct screen_browser *browser, struct mpdclient *c)
259 {
260 struct filelist_entry *entry = browser_get_selected_entry(browser);
261 struct mpd_entity *entity;
263 if (entry == NULL)
264 return false;
266 entity = entry->entity;
267 if (entity == NULL)
268 return false;
270 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_PLAYLIST)
271 return load_playlist(c, mpd_entity_get_playlist(entity));
272 else if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
273 return enqueue_and_play(c, entry);
274 return false;
275 }
277 static bool
278 browser_select_entry(struct mpdclient *c, struct filelist_entry *entry,
279 G_GNUC_UNUSED gboolean toggle)
280 {
281 assert(entry != NULL);
282 assert(entry->entity != NULL);
284 if (mpd_entity_get_type(entry->entity) == MPD_ENTITY_TYPE_PLAYLIST)
285 return load_playlist(c, mpd_entity_get_playlist(entry->entity));
287 if (mpd_entity_get_type(entry->entity) == MPD_ENTITY_TYPE_DIRECTORY) {
288 const struct mpd_directory *dir =
289 mpd_entity_get_directory(entry->entity);
291 if (mpdclient_cmd_add_path(c, mpd_directory_get_path(dir)) == 0) {
292 char *tmp = utf8_to_locale(mpd_directory_get_path(dir));
294 screen_status_printf(_("Adding \'%s\' to playlist"), tmp);
295 g_free(tmp);
296 }
298 return true;
299 }
301 if (mpd_entity_get_type(entry->entity) != MPD_ENTITY_TYPE_SONG)
302 return false;
304 #ifndef NCMPC_MINI
305 if (!toggle || (entry->flags & HIGHLIGHT) == 0)
306 #endif
307 {
308 const struct mpd_song *song =
309 mpd_entity_get_song(entry->entity);
311 #ifndef NCMPC_MINI
312 entry->flags |= HIGHLIGHT;
313 #endif
315 if (mpdclient_cmd_add(c, song) == 0) {
316 char buf[BUFSIZE];
318 strfsong(buf, BUFSIZE, options.list_format, song);
319 screen_status_printf(_("Adding \'%s\' to playlist"), buf);
320 }
321 #ifndef NCMPC_MINI
322 } else {
323 /* remove song from playlist */
324 const struct mpd_song *song =
325 mpd_entity_get_song(entry->entity);
326 int idx;
328 entry->flags &= ~HIGHLIGHT;
330 while ((idx = playlist_get_index_from_same_song(c, song)) >= 0)
331 mpdclient_cmd_delete(c, idx);
332 #endif
333 }
335 return true;
336 }
338 static bool
339 browser_handle_select(struct screen_browser *browser, struct mpdclient *c)
340 {
341 struct filelist_entry *entry;
343 if (browser->lw->range_selection) {
344 for (unsigned i = browser->lw->selected_start;
345 i <= browser->lw->selected_end; i++) {
346 entry = browser_get_index(browser, i);
348 if (entry != NULL && entry->entity != NULL)
349 browser_select_entry(c, entry, TRUE);
350 }
351 return false;
352 } else {
353 entry = browser_get_selected_entry(browser);
355 if (entry == NULL || entry->entity == NULL)
356 return false;
358 return browser_select_entry(c, entry, TRUE);
359 }
360 }
362 static bool
363 browser_handle_add(struct screen_browser *browser, struct mpdclient *c)
364 {
365 struct filelist_entry *entry;
367 if (browser->lw->range_selection) {
368 for (unsigned i = browser->lw->selected_start;
369 i <= browser->lw->selected_end; i++) {
370 entry = browser_get_index(browser, i);
372 if (entry != NULL && entry->entity != NULL)
373 browser_select_entry(c, entry, FALSE);
374 }
375 return false;
376 } else {
377 entry = browser_get_selected_entry(browser);
379 if (entry == NULL || entry->entity == NULL)
380 return false;
382 return browser_select_entry(c, entry, FALSE);
383 }
384 }
386 static void
387 browser_handle_select_all(struct screen_browser *browser, struct mpdclient *c)
388 {
389 guint i;
391 if (browser->filelist == NULL)
392 return;
394 for (i = 0; i < filelist_length(browser->filelist); ++i) {
395 struct filelist_entry *entry = filelist_get(browser->filelist, i);
397 if (entry != NULL && entry->entity != NULL)
398 browser_select_entry(c, entry, FALSE);
399 }
400 }
402 #ifdef HAVE_GETMOUSE
403 static int
404 browser_handle_mouse_event(struct screen_browser *browser, struct mpdclient *c)
405 {
406 int row;
407 unsigned prev_selected = browser->lw->selected;
408 unsigned long bstate;
409 int length;
411 if (browser->filelist)
412 length = filelist_length(browser->filelist);
413 else
414 length = 0;
416 if (screen_get_mouse_event(c, &bstate, &row) ||
417 list_window_mouse(browser->lw, length, bstate, row))
418 return 1;
420 browser->lw->selected = browser->lw->start + row;
421 list_window_check_selected(browser->lw, length);
423 if( bstate & BUTTON1_CLICKED ) {
424 if (prev_selected == browser->lw->selected)
425 browser_handle_enter(browser, c);
426 } else if (bstate & BUTTON3_CLICKED) {
427 if (prev_selected == browser->lw->selected)
428 browser_handle_select(browser, c);
429 }
431 return 1;
432 }
433 #endif
435 bool
436 browser_cmd(struct screen_browser *browser,
437 struct mpdclient *c, command_t cmd)
438 {
439 const struct mpd_song *song;
441 if (browser->filelist == NULL)
442 return false;
444 switch (cmd) {
445 case CMD_PLAY:
446 browser_handle_enter(browser, c);
447 return true;
449 case CMD_SELECT:
450 if (browser_handle_select(browser, c))
451 /* continue and select next item... */
452 cmd = CMD_LIST_NEXT;
454 /* call list_window_cmd to go to the next item */
455 break;
457 case CMD_ADD:
458 if (browser_handle_add(browser, c))
459 /* continue and select next item... */
460 cmd = CMD_LIST_NEXT;
462 /* call list_window_cmd to go to the next item */
463 break;
465 case CMD_SELECT_ALL:
466 browser_handle_select_all(browser, c);
467 return true;
469 case CMD_LIST_FIND:
470 case CMD_LIST_RFIND:
471 case CMD_LIST_FIND_NEXT:
472 case CMD_LIST_RFIND_NEXT:
473 screen_find(browser->lw, filelist_length(browser->filelist),
474 cmd, browser_lw_callback,
475 browser->filelist);
476 return true;
477 case CMD_LIST_JUMP:
478 screen_jump(browser->lw, browser_lw_callback, browser->filelist);
479 return true;
481 #ifdef HAVE_GETMOUSE
482 case CMD_MOUSE_EVENT:
483 browser_handle_mouse_event(browser, c);
484 return true;
485 #endif
487 #ifdef ENABLE_SONG_SCREEN
488 case CMD_SCREEN_SONG:
489 song = browser_get_selected_song(browser);
490 if (song == NULL)
491 return false;
493 screen_song_switch(c, song);
494 return true;
495 #endif
497 case CMD_LOCATE:
498 song = browser_get_selected_song(browser);
499 if (song == NULL)
500 return false;
502 screen_file_goto_song(c, song);
503 return true;
505 #ifdef ENABLE_LYRICS_SCREEN
506 case CMD_SCREEN_LYRICS:
507 song = browser_get_selected_song(browser);
508 if (song == NULL)
509 return false;
511 screen_lyrics_switch(c, song, false);
512 return true;
513 #endif
514 case CMD_SCREEN_SWAP:
515 screen_swap(c, browser_get_selected_song(browser));
516 return true;
518 default:
519 break;
520 }
522 if (list_window_cmd(browser->lw, filelist_length(browser->filelist),
523 cmd))
524 return true;
526 return false;
527 }