Code

Merge branch 'master' of git://git.musicpd.org/avuton/ncmpc
[ncmpc.git] / src / screen_queue.c
index 0381d52a0dbdeefb9fb0b2cbe0619feb2cc97360..c8fad68d2c7e52e2cf126a7aae6312ac094d7b51 100644 (file)
@@ -1,5 +1,5 @@
 /* ncmpc (Ncurses MPD Client)
- * (c) 2004-2009 The Music Player Daemon Project
+ * (c) 2004-2010 The Music Player Daemon Project
  * Project homepage: http://musicpd.org
  *
  * This program is free software; you can redistribute it and/or modify
@@ -30,7 +30,7 @@
 #include "utils.h"
 #include "strfsong.h"
 #include "wreadln.h"
-#include "colors.h"
+#include "song_paint.h"
 #include "screen.h"
 #include "screen_utils.h"
 #include "screen_song.h"
@@ -57,7 +57,6 @@ typedef struct
 } completion_callback_data_t;
 
 static struct hscroll hscroll;
-static guint scroll_source_id;
 #endif
 
 static struct mpdclient_playlist *playlist;
@@ -118,21 +117,8 @@ screen_queue_restore_selection(void)
        screen_queue_save_selection();
 }
 
-#ifndef NCMPC_MINI
-static gboolean
-scroll_timer_callback(G_GNUC_UNUSED gpointer data)
-{
-       scroll_source_id = 0;
-
-       hscroll_step(&hscroll);
-       screen_queue_repaint();
-       return false;
-}
-#endif
-
 static const char *
-screen_queue_lw_callback(unsigned idx, bool *highlight, char **second_column,
-                        G_GNUC_UNUSED void *data)
+screen_queue_lw_callback(unsigned idx, G_GNUC_UNUSED void *data)
 {
        static char songname[MAX_SONG_LENGTH];
        struct mpd_song *song;
@@ -141,83 +127,27 @@ screen_queue_lw_callback(unsigned idx, bool *highlight, char **second_column,
        assert(idx < playlist_length(playlist));
 
        song = playlist_get(playlist, idx);
-       if ((int)mpd_song_get_id(song) == current_song_id)
-               *highlight = true;
 
        strfsong(songname, MAX_SONG_LENGTH, options.list_format, song);
 
-#ifndef NCMPC_MINI
-       if (second_column != NULL && mpd_song_get_duration(song) > 0) {
-               char duration[32];
-               format_duration_short(duration, sizeof(duration),
-                                     mpd_song_get_duration(song));
-               *second_column = g_strdup(duration);
-       }
-
-       if (idx == lw->selected)
-       {
-               int second_column_len = 0;
-               if (second_column != NULL && *second_column != NULL)
-                       second_column_len = strlen(*second_column);
-               if (options.scroll && utf8_width(songname) > (unsigned)(COLS - second_column_len - 1) )
-               {
-                       static unsigned current_song;
-                       char *tmp;
-
-                       if (current_song != lw->selected) {
-                               hscroll_reset(&hscroll);
-                               current_song = lw->selected;
-                       }
-
-                       tmp = strscroll(&hscroll, songname, options.scroll_sep,
-                                       MAX_SONG_LENGTH);
-                       g_strlcpy(songname, tmp, MAX_SONG_LENGTH);
-                       g_free(tmp);
-
-                       if (scroll_source_id == 0)
-                               scroll_source_id =
-                                       g_timeout_add(1000,
-                                                     scroll_timer_callback,
-                                                     NULL);
-               } else {
-                       hscroll_reset(&hscroll);
-
-                       if (scroll_source_id != 0) {
-                               g_source_remove(scroll_source_id);
-                               scroll_source_id = 0;
-                       }
-               }
-       }
-#else
-       (void)second_column;
-#endif
-
        return songname;
 }
 
 static void
-center_playing_item(struct mpdclient *c, bool center_cursor)
+center_playing_item(const struct mpd_status *status, bool center_cursor)
 {
-       const struct mpd_song *song;
-       unsigned length = c->playlist.list->len;
        int idx;
 
-       song = mpdclient_get_current_song(c);
-       if (song == NULL)
+       if (status == NULL ||
+           (mpd_status_get_state(status) != MPD_STATE_PLAY &&
+            mpd_status_get_state(status) != MPD_STATE_PAUSE))
                return;
 
        /* try to center the song that are playing */
-       idx = playlist_get_index(&c->playlist, c->song);
+       idx = mpd_status_get_song_pos(status);
        if (idx < 0)
                return;
 
-       if (length < lw->rows)
-       {
-               if (center_cursor)
-                       list_window_set_cursor(lw, idx);
-               return;
-       }
-
        list_window_center(lw, idx);
 
        if (center_cursor) {
@@ -229,6 +159,32 @@ center_playing_item(struct mpdclient *c, bool center_cursor)
        list_window_fetch_cursor(lw);
 }
 
+G_GNUC_PURE
+static int
+get_current_song_id(const struct mpd_status *status)
+{
+       return status != NULL &&
+               (mpd_status_get_state(status) == MPD_STATE_PLAY ||
+                mpd_status_get_state(status) == MPD_STATE_PAUSE)
+               ? (int)mpd_status_get_song_id(status)
+               : -1;
+}
+
+static bool
+screen_queue_song_change(const struct mpd_status *status)
+{
+       if (get_current_song_id(status) == current_song_id)
+               return false;
+
+       current_song_id = get_current_song_id(status);
+
+       /* center the cursor */
+       if (options.auto_center && !lw->range_selection)
+               center_playing_item(status, false);
+
+       return true;
+}
+
 #ifndef NCMPC_MINI
 static void
 save_pre_completion_cb(GCompletion *gcmp, G_GNUC_UNUSED gchar *line,
@@ -319,9 +275,11 @@ playlist_save(struct mpdclient *c, char *name, char *defaultname)
 
        /* send save command to mpd */
 
-       filename_utf8 = locale_to_utf8(filename);
-
        connection = mpdclient_get_connection(c);
+       if (connection == NULL)
+               return -1;
+
+       filename_utf8 = locale_to_utf8(filename);
        if (!mpd_run_save(connection, filename_utf8)) {
                if (mpd_connection_get_error(connection) == MPD_ERROR_SERVER &&
                    mpd_connection_get_server_error(connection) == MPD_SERVER_ERROR_EXIST &&
@@ -418,8 +376,8 @@ static int
 handle_add_to_playlist(struct mpdclient *c)
 {
        gchar *path;
-#ifndef NCMPC_MINI
        GCompletion *gcmp;
+#ifndef NCMPC_MINI
        GList *list = NULL;
        GList *dir_list = NULL;
        completion_callback_data_t data;
@@ -433,18 +391,15 @@ handle_add_to_playlist(struct mpdclient *c)
        wrln_completion_callback_data = &data;
        wrln_pre_completion_callback = add_pre_completion_cb;
        wrln_post_completion_callback = add_post_completion_cb;
+#else
+       gcmp = NULL;
 #endif
 
        /* get path */
        path = screen_readln(_("Add"),
                             NULL,
                             NULL,
-#ifdef NCMPC_MINI
-                            NULL
-#else
-                            gcmp
-#endif
-                            );
+                            gcmp);
 
        /* destroy completion data */
 #ifndef NCMPC_MINI
@@ -471,6 +426,11 @@ static void
 screen_queue_init(WINDOW *w, int cols, int rows)
 {
        lw = list_window_init(w, cols, rows);
+
+#ifndef NCMPC_MINI
+       if (options.scroll)
+               hscroll_init(&hscroll, w, options.scroll_sep);
+#endif
 }
 
 static gboolean
@@ -509,6 +469,7 @@ screen_queue_open(struct mpdclient *c)
        }
 
        screen_queue_restore_selection();
+       screen_queue_song_change(c->status);
 }
 
 static void
@@ -518,6 +479,11 @@ screen_queue_close(void)
                g_source_remove(timer_hide_cursor_id);
                timer_hide_cursor_id = 0;
        }
+
+#ifndef NCMPC_MINI
+       if (options.scroll)
+               hscroll_clear(&hscroll);
+#endif
 }
 
 static void
@@ -543,38 +509,58 @@ screen_queue_title(char *str, size_t size)
        return str;
 }
 
+static void
+screen_queue_paint_callback(WINDOW *w, unsigned i,
+                           unsigned y, unsigned width,
+                           bool selected, G_GNUC_UNUSED void *data)
+{
+       const struct mpd_song *song;
+       struct hscroll *row_hscroll;
+
+       assert(playlist != NULL);
+       assert(i < playlist_length(playlist));
+
+       song = playlist_get(playlist, i);
+
+#ifdef NCMPC_MINI
+       row_hscroll = NULL;
+#else
+       row_hscroll = selected && options.scroll && lw->selected == i
+               ? &hscroll : NULL;
+#endif
+
+       paint_song_row(w, y, width, selected,
+                      (int)mpd_song_get_id(song) == current_song_id,
+                      song, row_hscroll);
+}
+
 static void
 screen_queue_paint(void)
 {
-       list_window_paint(lw, screen_queue_lw_callback, NULL);
+#ifndef NCMPC_MINI
+       if (options.scroll)
+               hscroll_clear(&hscroll);
+#endif
+
+       list_window_paint2(lw, screen_queue_paint_callback, NULL);
 }
 
 static void
 screen_queue_update(struct mpdclient *c)
 {
-       static int prev_song_id = -1;
-
-       if (c->events & MPD_IDLE_PLAYLIST)
+       if (c->events & MPD_IDLE_QUEUE)
                screen_queue_restore_selection();
-
-       current_song_id = c->status != NULL &&
-               (mpd_status_get_state(c->status) == MPD_STATE_PLAY ||
-                mpd_status_get_state(c->status) == MPD_STATE_PAUSE)
-               ? (int)mpd_status_get_song_id(c->status) : -1;
-
-       if (current_song_id != prev_song_id) {
-               prev_song_id = current_song_id;
-
-               /* center the cursor */
-               if (options.auto_center && current_song_id != -1 && ! lw->range_selection)
-                       center_playing_item(c, false);
-
-               screen_queue_repaint();
-       } else if (c->events & MPD_IDLE_PLAYLIST) {
-               /* the playlist has changed, we must paint the new
-                  version */
+       else
+               /* the queue size may have changed, even if we havn't
+                  revceived the QUEUE idle event yet */
+               list_window_set_length(lw, playlist_length(playlist));
+
+       if (((c->events & MPD_IDLE_PLAYER) != 0 &&
+            screen_queue_song_change(c->status)) ||
+           c->events & MPD_IDLE_QUEUE)
+               /* the queue or the current song has changed, we must
+                  paint the new version */
                screen_queue_repaint();
-       }
 }
 
 #ifdef HAVE_GETMOUSE
@@ -582,8 +568,8 @@ static bool
 handle_mouse_event(struct mpdclient *c)
 {
        int row;
-       unsigned selected;
        unsigned long bstate;
+       unsigned old_selected;
 
        if (screen_get_mouse_event(c, &bstate, &row) ||
            list_window_mouse(lw, bstate, row)) {
@@ -597,19 +583,29 @@ handle_mouse_event(struct mpdclient *c)
                return true;
        }
 
-       selected = lw->start + row;
+       old_selected = lw->selected;
+       list_window_set_cursor(lw, lw->start + row);
 
        if (bstate & BUTTON1_CLICKED) {
                /* play */
-               if (lw->start + row < playlist_length(playlist))
-                       mpdclient_cmd_play(c, lw->start + row);
+               const struct mpd_song *song = screen_queue_selected_song();
+               if (song != NULL) {
+                       struct mpd_connection *connection =
+                               mpdclient_get_connection(c);
+
+                       if (connection != NULL &&
+                           !mpd_run_play_id(connection,
+                                            mpd_song_get_id(song)))
+                               mpdclient_handle_error(c);
+               }
        } else if (bstate & BUTTON3_CLICKED) {
                /* delete */
-               if (selected == lw->selected)
+               if (lw->selected == old_selected)
                        mpdclient_cmd_delete(c, lw->selected);
+
+               list_window_set_length(lw, playlist_length(playlist));
        }
 
-       list_window_set_cursor(lw, selected);
        screen_queue_save_selection();
        screen_queue_repaint();
 
@@ -624,6 +620,7 @@ screen_queue_cmd(struct mpdclient *c, command_t cmd)
        static command_t cached_cmd = CMD_NONE;
        command_t prev_cmd = cached_cmd;
        struct list_window_range range;
+       const struct mpd_song *song;
 
        cached_cmd = cmd;
 
@@ -644,7 +641,7 @@ screen_queue_cmd(struct mpdclient *c, command_t cmd)
 
        switch(cmd) {
        case CMD_SCREEN_UPDATE:
-               center_playing_item(c, prev_cmd == CMD_SCREEN_UPDATE);
+               center_playing_item(c->status, prev_cmd == CMD_SCREEN_UPDATE);
                screen_queue_repaint();
                return false;
        case CMD_SELECT_PLAYING:
@@ -713,7 +710,15 @@ screen_queue_cmd(struct mpdclient *c, command_t cmd)
 
        switch(cmd) {
        case CMD_PLAY:
-               mpdclient_cmd_play(c, lw->selected);
+               song = screen_queue_selected_song();
+               if (song == NULL)
+                       return false;
+
+               connection = mpdclient_get_connection(c);
+               if (connection != NULL &&
+                   !mpd_run_play_id(connection, mpd_song_get_id(song)))
+                       mpdclient_handle_error(c);
+
                return true;
 
        case CMD_DELETE:
@@ -738,6 +743,9 @@ screen_queue_cmd(struct mpdclient *c, command_t cmd)
                        break;
 
                connection = mpdclient_get_connection(c);
+               if (connection == NULL)
+                       return true;
+
                if (mpd_run_shuffle_range(connection, range.start, range.end))
                        screen_status_message(_("Shuffled playlist"));
                else
@@ -749,8 +757,8 @@ screen_queue_cmd(struct mpdclient *c, command_t cmd)
                if (range.start == 0 || range.end <= range.start)
                        return false;
 
-               for (unsigned i = range.start; i < range.end; ++i)
-                       mpdclient_cmd_move(c, i, i - 1);
+               if (!mpdclient_cmd_move(c, range.end - 1, range.start - 1))
+                       return true;
 
                lw->selected--;
                lw->range_base--;
@@ -763,8 +771,8 @@ screen_queue_cmd(struct mpdclient *c, command_t cmd)
                if (range.end >= playlist_length(&c->playlist))
                        return false;
 
-               for (int i = range.end - 1; i >= (int)range.start; --i)
-                       mpdclient_cmd_move(c, i, i + 1);
+               if (!mpdclient_cmd_move(c, range.start, range.end))
+                       return true;
 
                lw->selected++;
                lw->range_base++;