Code

mpdclient: removed the mpdclient_t typedef
[ncmpc.git] / src / screen_browser.c
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         }
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)
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!";
174 static bool
175 load_playlist(struct mpdclient *c, const struct mpd_playlist *playlist)
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;
186 static bool
187 enqueue_and_play(struct mpdclient *c, struct filelist_entry *entry)
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;
215 struct filelist_entry *
216 browser_get_selected_entry(const struct screen_browser *browser)
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);
226 static const struct mpd_entity *
227 browser_get_selected_entity(const struct screen_browser *browser)
229         const struct filelist_entry *entry = browser_get_selected_entry(browser);
231         return entry != NULL
232                 ? entry->entity
233                 : NULL;
236 static const struct mpd_song *
237 browser_get_selected_song(const struct screen_browser *browser)
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;
247 static struct filelist_entry *
248 browser_get_index(const struct screen_browser *browser, unsigned i)
250         if (browser->filelist == NULL ||
251             i >= filelist_length(browser->filelist))
252                 return NULL;
254         return filelist_get(browser->filelist, i);
257 static bool
258 browser_handle_enter(struct screen_browser *browser, struct mpdclient *c)
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;
277 static bool
278 browser_select_entry(struct mpdclient *c, struct filelist_entry *entry,
279                      G_GNUC_UNUSED gboolean toggle)
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;
338 static bool
339 browser_handle_select(struct screen_browser *browser, struct mpdclient *c)
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         }
362 static bool
363 browser_handle_add(struct screen_browser *browser, struct mpdclient *c)
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         }
386 static void
387 browser_handle_select_all(struct screen_browser *browser, struct mpdclient *c)
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         }
402 #ifdef HAVE_GETMOUSE
403 static int
404 browser_handle_mouse_event(struct screen_browser *browser, struct mpdclient *c)
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;
433 #endif
435 bool
436 browser_cmd(struct screen_browser *browser,
437             struct mpdclient *c, command_t cmd)
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;