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 "screen_file.h"
22 #include "i18n.h"
23 #include "options.h"
24 #include "charset.h"
25 #include "strfsong.h"
26 #include "screen_utils.h"
27 #include "mpdclient.h"
28 #include "filelist.h"
30 #include <mpd/client.h>
32 #include <string.h>
34 #define BUFSIZE 1024
36 #ifndef NCMPC_MINI
37 #define HIGHLIGHT (0x01)
38 #endif
40 static const char playlist_format[] = "*%s*";
42 #ifndef NCMPC_MINI
44 /* sync highlight flags with playlist */
45 void
46 screen_browser_sync_highlights(struct filelist *fl,
47 const struct mpdclient_playlist *playlist)
48 {
49 guint i;
51 for (i = 0; i < filelist_length(fl); ++i) {
52 struct filelist_entry *entry = filelist_get(fl, i);
53 struct mpd_entity *entity = entry->entity;
55 if (entity != NULL && mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
56 const struct mpd_song *song =
57 mpd_entity_get_song(entity);
59 if (playlist_get_index_from_same_song(playlist,
60 song) >= 0)
61 entry->flags |= HIGHLIGHT;
62 else
63 entry->flags &= ~HIGHLIGHT;
64 }
65 }
66 }
68 #endif
70 /* list_window callback */
71 const char *
72 browser_lw_callback(unsigned idx, bool *highlight, G_GNUC_UNUSED char **second_column, void *data)
73 {
74 struct filelist *fl = (struct filelist *) data;
75 static char buf[BUFSIZE];
76 struct filelist_entry *entry;
77 struct mpd_entity *entity;
79 if (fl == NULL || idx >= filelist_length(fl))
80 return NULL;
82 entry = filelist_get(fl, idx);
83 assert(entry != NULL);
85 entity = entry->entity;
86 #ifndef NCMPC_MINI
87 *highlight = (entry->flags & HIGHLIGHT) != 0;
88 #else
89 *highlight = false;
90 #endif
92 if( entity == NULL )
93 return "[..]";
95 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_DIRECTORY) {
96 const struct mpd_directory *dir =
97 mpd_entity_get_directory(entity);
98 char *directory = utf8_to_locale(g_basename(mpd_directory_get_path(dir)));
100 g_snprintf(buf, BUFSIZE, "[%s]", directory);
101 g_free(directory);
102 return buf;
103 } else if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
104 const struct mpd_song *song = mpd_entity_get_song(entity);
106 strfsong(buf, BUFSIZE, options.list_format, song);
107 return buf;
108 } else if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_PLAYLIST) {
109 const struct mpd_playlist *playlist =
110 mpd_entity_get_playlist(entity);
111 char *filename = utf8_to_locale(g_basename(mpd_playlist_get_path(playlist)));
113 g_snprintf(buf, BUFSIZE, playlist_format, filename);
114 g_free(filename);
115 return buf;
116 }
118 return "Error: Unknown entry!";
119 }
121 static bool
122 load_playlist(struct mpdclient *c, const struct mpd_playlist *playlist)
123 {
124 char *filename = utf8_to_locale(mpd_playlist_get_path(playlist));
126 if (mpdclient_cmd_load_playlist(c, mpd_playlist_get_path(playlist)) == 0)
127 screen_status_printf(_("Loading playlist %s..."),
128 g_basename(filename));
129 g_free(filename);
130 return true;
131 }
133 static bool
134 enqueue_and_play(struct mpdclient *c, struct filelist_entry *entry)
135 {
136 const struct mpd_song *song = mpd_entity_get_song(entry->entity);
137 int id;
139 #ifndef NCMPC_MINI
140 if (!(entry->flags & HIGHLIGHT))
141 id = -1;
142 else
143 #endif
144 id = playlist_get_id_from_same_song(&c->playlist, song);
146 if (id < 0) {
147 char buf[BUFSIZE];
149 id = mpd_run_add_id(c->connection, mpd_song_get_uri(song));
150 if (id < 0) {
151 mpdclient_handle_error(c);
152 return false;
153 }
155 #ifndef NCMPC_MINI
156 entry->flags |= HIGHLIGHT;
157 #endif
158 strfsong(buf, BUFSIZE, options.list_format, song);
159 screen_status_printf(_("Adding \'%s\' to playlist"), buf);
160 }
162 if (!mpd_run_play_id(c->connection, id)) {
163 mpdclient_handle_error(c);
164 return false;
165 }
167 return true;
168 }
170 struct filelist_entry *
171 browser_get_selected_entry(const struct screen_browser *browser)
172 {
173 if (browser->filelist == NULL ||
174 browser->lw->selected_start < browser->lw->selected_end ||
175 browser->lw->selected >= filelist_length(browser->filelist))
176 return NULL;
178 return filelist_get(browser->filelist, browser->lw->selected);
179 }
181 static const struct mpd_entity *
182 browser_get_selected_entity(const struct screen_browser *browser)
183 {
184 const struct filelist_entry *entry = browser_get_selected_entry(browser);
186 return entry != NULL
187 ? entry->entity
188 : NULL;
189 }
191 static const struct mpd_song *
192 browser_get_selected_song(const struct screen_browser *browser)
193 {
194 const struct mpd_entity *entity = browser_get_selected_entity(browser);
196 return entity != NULL &&
197 mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG
198 ? mpd_entity_get_song(entity)
199 : NULL;
200 }
202 static struct filelist_entry *
203 browser_get_index(const struct screen_browser *browser, unsigned i)
204 {
205 if (browser->filelist == NULL ||
206 i >= filelist_length(browser->filelist))
207 return NULL;
209 return filelist_get(browser->filelist, i);
210 }
212 static bool
213 browser_handle_enter(struct screen_browser *browser, struct mpdclient *c)
214 {
215 struct filelist_entry *entry = browser_get_selected_entry(browser);
216 struct mpd_entity *entity;
218 if (entry == NULL)
219 return false;
221 entity = entry->entity;
222 if (entity == NULL)
223 return false;
225 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_PLAYLIST)
226 return load_playlist(c, mpd_entity_get_playlist(entity));
227 else if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
228 return enqueue_and_play(c, entry);
229 return false;
230 }
232 static bool
233 browser_select_entry(struct mpdclient *c, struct filelist_entry *entry,
234 G_GNUC_UNUSED gboolean toggle)
235 {
236 assert(entry != NULL);
237 assert(entry->entity != NULL);
239 if (mpd_entity_get_type(entry->entity) == MPD_ENTITY_TYPE_PLAYLIST)
240 return load_playlist(c, mpd_entity_get_playlist(entry->entity));
242 if (mpd_entity_get_type(entry->entity) == MPD_ENTITY_TYPE_DIRECTORY) {
243 const struct mpd_directory *dir =
244 mpd_entity_get_directory(entry->entity);
246 if (mpdclient_cmd_add_path(c, mpd_directory_get_path(dir)) == 0) {
247 char *tmp = utf8_to_locale(mpd_directory_get_path(dir));
249 screen_status_printf(_("Adding \'%s\' to playlist"), tmp);
250 g_free(tmp);
251 }
253 return true;
254 }
256 if (mpd_entity_get_type(entry->entity) != MPD_ENTITY_TYPE_SONG)
257 return false;
259 #ifndef NCMPC_MINI
260 if (!toggle || (entry->flags & HIGHLIGHT) == 0)
261 #endif
262 {
263 const struct mpd_song *song =
264 mpd_entity_get_song(entry->entity);
266 #ifndef NCMPC_MINI
267 entry->flags |= HIGHLIGHT;
268 #endif
270 if (mpdclient_cmd_add(c, song) == 0) {
271 char buf[BUFSIZE];
273 strfsong(buf, BUFSIZE, options.list_format, song);
274 screen_status_printf(_("Adding \'%s\' to playlist"), buf);
275 }
276 #ifndef NCMPC_MINI
277 } else {
278 /* remove song from playlist */
279 const struct mpd_song *song =
280 mpd_entity_get_song(entry->entity);
281 int idx;
283 entry->flags &= ~HIGHLIGHT;
285 while ((idx = playlist_get_index_from_same_song(&c->playlist,
286 song)) >= 0)
287 mpdclient_cmd_delete(c, idx);
288 #endif
289 }
291 return true;
292 }
294 static bool
295 browser_handle_select(struct screen_browser *browser, struct mpdclient *c)
296 {
297 struct filelist_entry *entry;
299 if (browser->lw->range_selection) {
300 for (unsigned i = browser->lw->selected_start;
301 i <= browser->lw->selected_end; i++) {
302 entry = browser_get_index(browser, i);
304 if (entry != NULL && entry->entity != NULL)
305 browser_select_entry(c, entry, TRUE);
306 }
307 return false;
308 } else {
309 entry = browser_get_selected_entry(browser);
311 if (entry == NULL || entry->entity == NULL)
312 return false;
314 return browser_select_entry(c, entry, TRUE);
315 }
316 }
318 static bool
319 browser_handle_add(struct screen_browser *browser, struct mpdclient *c)
320 {
321 struct filelist_entry *entry;
323 if (browser->lw->range_selection) {
324 for (unsigned i = browser->lw->selected_start;
325 i <= browser->lw->selected_end; i++) {
326 entry = browser_get_index(browser, i);
328 if (entry != NULL && entry->entity != NULL)
329 browser_select_entry(c, entry, FALSE);
330 }
331 return false;
332 } else {
333 entry = browser_get_selected_entry(browser);
335 if (entry == NULL || entry->entity == NULL)
336 return false;
338 return browser_select_entry(c, entry, FALSE);
339 }
340 }
342 static void
343 browser_handle_select_all(struct screen_browser *browser, struct mpdclient *c)
344 {
345 guint i;
347 if (browser->filelist == NULL)
348 return;
350 for (i = 0; i < filelist_length(browser->filelist); ++i) {
351 struct filelist_entry *entry = filelist_get(browser->filelist, i);
353 if (entry != NULL && entry->entity != NULL)
354 browser_select_entry(c, entry, FALSE);
355 }
356 }
358 #ifdef HAVE_GETMOUSE
359 static int
360 browser_handle_mouse_event(struct screen_browser *browser, struct mpdclient *c)
361 {
362 int row;
363 unsigned prev_selected = browser->lw->selected;
364 unsigned long bstate;
365 int length;
367 if (browser->filelist)
368 length = filelist_length(browser->filelist);
369 else
370 length = 0;
372 if (screen_get_mouse_event(c, &bstate, &row) ||
373 list_window_mouse(browser->lw, length, bstate, row))
374 return 1;
376 browser->lw->selected = browser->lw->start + row;
377 list_window_check_selected(browser->lw, length);
379 if( bstate & BUTTON1_CLICKED ) {
380 if (prev_selected == browser->lw->selected)
381 browser_handle_enter(browser, c);
382 } else if (bstate & BUTTON3_CLICKED) {
383 if (prev_selected == browser->lw->selected)
384 browser_handle_select(browser, c);
385 }
387 return 1;
388 }
389 #endif
391 bool
392 browser_cmd(struct screen_browser *browser,
393 struct mpdclient *c, command_t cmd)
394 {
395 const struct mpd_song *song;
397 if (browser->filelist == NULL)
398 return false;
400 switch (cmd) {
401 case CMD_PLAY:
402 browser_handle_enter(browser, c);
403 return true;
405 case CMD_SELECT:
406 if (browser_handle_select(browser, c))
407 /* continue and select next item... */
408 cmd = CMD_LIST_NEXT;
410 /* call list_window_cmd to go to the next item */
411 break;
413 case CMD_ADD:
414 if (browser_handle_add(browser, c))
415 /* continue and select next item... */
416 cmd = CMD_LIST_NEXT;
418 /* call list_window_cmd to go to the next item */
419 break;
421 case CMD_SELECT_ALL:
422 browser_handle_select_all(browser, c);
423 return true;
425 case CMD_LIST_FIND:
426 case CMD_LIST_RFIND:
427 case CMD_LIST_FIND_NEXT:
428 case CMD_LIST_RFIND_NEXT:
429 screen_find(browser->lw, filelist_length(browser->filelist),
430 cmd, browser_lw_callback,
431 browser->filelist);
432 return true;
433 case CMD_LIST_JUMP:
434 screen_jump(browser->lw, browser_lw_callback, browser->filelist);
435 return true;
437 #ifdef HAVE_GETMOUSE
438 case CMD_MOUSE_EVENT:
439 browser_handle_mouse_event(browser, c);
440 return true;
441 #endif
443 #ifdef ENABLE_SONG_SCREEN
444 case CMD_SCREEN_SONG:
445 song = browser_get_selected_song(browser);
446 if (song == NULL)
447 return false;
449 screen_song_switch(c, song);
450 return true;
451 #endif
453 case CMD_LOCATE:
454 song = browser_get_selected_song(browser);
455 if (song == NULL)
456 return false;
458 screen_file_goto_song(c, song);
459 return true;
461 #ifdef ENABLE_LYRICS_SCREEN
462 case CMD_SCREEN_LYRICS:
463 song = browser_get_selected_song(browser);
464 if (song == NULL)
465 return false;
467 screen_lyrics_switch(c, song, false);
468 return true;
469 #endif
470 case CMD_SCREEN_SWAP:
471 screen_swap(c, browser_get_selected_song(browser));
472 return true;
474 default:
475 break;
476 }
478 if (list_window_cmd(browser->lw, filelist_length(browser->filelist),
479 cmd))
480 return true;
482 return false;
483 }