screen_artist: split into three Page implementations
[ncmpc-debian.git] / src / screen_artist.cxx
index 30de3df..77a3927 100644 (file)
  */
 
 #include "screen_artist.hxx"
+#include "ArtistListPage.hxx"
+#include "AlbumListPage.hxx"
 #include "screen_interface.hxx"
 #include "screen_status.hxx"
 #include "screen_find.hxx"
 #include "screen_browser.hxx"
+#include "screen.hxx"
+#include "ProxyPage.hxx"
 #include "i18n.h"
 #include "charset.hxx"
 #include "mpdclient.hxx"
 
 #define BUFSIZE 1024
 
-class ArtistBrowserPage final : public FileListPage {
-       enum class Mode {
-               ARTISTS,
-               ALBUMS,
-               SONGS,
-       } mode = Mode::ARTISTS;
-
-       std::vector<std::string> artist_list, album_list;
+class SongListPage final : public FileListPage {
        std::string artist;
 
        /**
@@ -57,185 +54,86 @@ class ArtistBrowserPage final : public FileListPage {
        std::string album;
 
 public:
-       ArtistBrowserPage(ScreenManager &_screen, WINDOW *_w,
-                         Size size)
-               :FileListPage(_screen, _w, size,
-                             options.list_format) {}
+       SongListPage(ScreenManager &_screen, WINDOW *_w, Size size)
+               :FileListPage(_screen, _w, size, options.list_format) {}
 
-private:
-       void FreeLists();
-       void LoadArtistList(struct mpdclient &c);
-       void LoadAlbumList(struct mpdclient &c);
-       void LoadSongList(struct mpdclient &c);
-       void Reload(struct mpdclient &c);
+       template<typename A>
+       void SetArtist(A &&_artist) {
+               artist = std::forward<A>(_artist);
+               AddPendingEvents(~0u);
+       }
 
-       void OpenArtistList(struct mpdclient &c);
-       void OpenArtistList(struct mpdclient &c, const char *selected_value);
-       void OpenAlbumList(struct mpdclient &c, std::string _artist);
-       void OpenAlbumList(struct mpdclient &c, std::string _artist,
-                          const char *selected_value);
-       void OpenSongList(struct mpdclient &c, std::string _artist,
-                         std::string _album);
+       const std::string &GetArtist() {
+               return artist;
+       }
 
-       bool OnListCommand(struct mpdclient &c, command_t cmd);
+       template<typename A>
+       void SetAlbum(A &&_album) {
+               album = std::forward<A>(_album);
+               AddPendingEvents(~0u);
+       }
+
+       const std::string &GetAlbum() {
+               return album;
+       }
+
+       void LoadSongList(struct mpdclient &c);
 
-public:
        /* virtual methods from class Page */
-       void OnOpen(struct mpdclient &c) override;
-       void Paint() const override;
        void Update(struct mpdclient &c, unsigned events) override;
        bool OnCommand(struct mpdclient &c, command_t cmd) override;
        const char *GetTitle(char *s, size_t size) const override;
 };
 
-gcc_pure
-static int
-string_array_find(const std::vector<std::string> &array, const char *value)
-{
-       for (size_t i = 0; i < array.size(); ++i)
-               if (array[i] == value)
-                       return i;
-
-       return -1;
-}
-
-gcc_pure
-static bool
-CompareUTF8(const std::string &a, const std::string &b)
-{
-       char *key1 = g_utf8_collate_key(a.c_str(), -1);
-       char *key2 = g_utf8_collate_key(b.c_str(), -1);
-       int n = strcmp(key1,key2);
-       g_free(key1);
-       g_free(key2);
-       return n < 0;
-}
-
-/* list_window callback */
-static const char *
-screen_artist_lw_callback(unsigned idx, void *data)
-{
-       const auto &list = *(const std::vector<std::string> *)data;
-
-       /*
-       if (mode == Mode::ALBUMS) {
-               if (idx == 0)
-                       return "..";
-               else if (idx == list.size() + 1)
-                       return _("All tracks");
-
-               --idx;
-       }
-       */
-
-       assert(idx < list.size());
-
-       const char *str_utf8 = list[idx].c_str();
-       char *str = utf8_to_locale(str_utf8);
-
-       static char buf[BUFSIZE];
-       g_strlcpy(buf, str, sizeof(buf));
-       g_free(str);
-
-       return buf;
-}
-
-/* list_window callback */
-static const char *
-AlbumListCallback(unsigned idx, void *data)
-{
-       const auto &list = *(const std::vector<std::string> *)data;
-
-       if (idx == 0)
-               return "..";
-       else if (idx == list.size() + 1)
-               return _("All tracks");
-
-       --idx;
-
-       return screen_artist_lw_callback(idx, data);
-}
-
-void
-ArtistBrowserPage::FreeLists()
-{
-       artist_list.clear();
-       album_list.clear();
-
-       delete filelist;
-       filelist = nullptr;
-}
-
-static void
-recv_tag_values(struct mpd_connection *connection, enum mpd_tag_type tag,
-               std::vector<std::string> &list)
-{
-       struct mpd_pair *pair;
-
-       while ((pair = mpd_recv_pair_tag(connection, tag)) != nullptr) {
-               list.emplace_back(pair->value);
-               mpd_return_pair(connection, pair);
-       }
-}
-
 void
-ArtistBrowserPage::LoadArtistList(struct mpdclient &c)
+SongListPage::Update(struct mpdclient &c, unsigned events)
 {
-       struct mpd_connection *connection = mpdclient_get_connection(&c);
-
-       assert(mode == Mode::ARTISTS);
-       assert(artist_list.empty());
-       assert(album_list.empty());
-       assert(filelist == nullptr);
-
-       artist_list.clear();
-
-       if (connection != nullptr) {
-               mpd_search_db_tags(connection, MPD_TAG_ARTIST);
-               mpd_search_commit(connection);
-               recv_tag_values(connection, MPD_TAG_ARTIST, artist_list);
-
-               mpdclient_finish_command(&c);
+       if (events & MPD_IDLE_DATABASE) {
+               LoadSongList(c);
        }
-
-       /* sort list */
-       std::sort(artist_list.begin(), artist_list.end(), CompareUTF8);
-       lw.SetLength(artist_list.size());
 }
 
-void
-ArtistBrowserPage::LoadAlbumList(struct mpdclient &c)
-{
-       struct mpd_connection *connection = mpdclient_get_connection(&c);
-
-       assert(mode == Mode::ALBUMS);
-       assert(album_list.empty());
-       assert(filelist == nullptr);
+class ArtistBrowserPage final : public ProxyPage {
+       enum class Mode {
+               ARTISTS,
+               ALBUMS,
+               SONGS,
+       } mode = Mode::ARTISTS;
 
-       if (connection != nullptr) {
-               mpd_search_db_tags(connection, MPD_TAG_ALBUM);
-               mpd_search_add_tag_constraint(connection,
-                                             MPD_OPERATOR_DEFAULT,
-                                             MPD_TAG_ARTIST, artist.c_str());
-               mpd_search_commit(connection);
+       ArtistListPage artist_list_page;
+       AlbumListPage album_list_page;
+       SongListPage song_list_page;
 
-               recv_tag_values(connection, MPD_TAG_ALBUM, album_list);
+public:
+       ArtistBrowserPage(ScreenManager &_screen, WINDOW *_w,
+                         Size size)
+               :ProxyPage(_w),
+                artist_list_page(_screen, _w, size),
+                album_list_page(_screen, _w, size),
+                song_list_page(_screen, _w, size) {}
 
-               mpdclient_finish_command(&c);
-       }
+private:
+       void OpenArtistList(struct mpdclient &c);
+       void OpenArtistList(struct mpdclient &c, const char *selected_value);
+       void OpenAlbumList(struct mpdclient &c, std::string _artist);
+       void OpenAlbumList(struct mpdclient &c, std::string _artist,
+                          const char *selected_value);
+       void OpenSongList(struct mpdclient &c, std::string _artist,
+                         std::string _album);
 
-       /* sort list */
-       std::sort(album_list.begin(), album_list.end(), CompareUTF8);
-       lw.SetLength(album_list.size() + 2);
-}
+public:
+       /* virtual methods from class Page */
+       void OnOpen(struct mpdclient &c) override;
+       void Update(struct mpdclient &c, unsigned events) override;
+       bool OnCommand(struct mpdclient &c, command_t cmd) override;
+};
 
 void
-ArtistBrowserPage::LoadSongList(struct mpdclient &c)
+SongListPage::LoadSongList(struct mpdclient &c)
 {
        struct mpd_connection *connection = mpdclient_get_connection(&c);
 
-       assert(mode == Mode::SONGS);
-       assert(filelist == nullptr);
+       delete filelist;
 
        filelist = new FileList();
        /* add a dummy entry for ".." */
@@ -263,10 +161,8 @@ ArtistBrowserPage::LoadSongList(struct mpdclient &c)
 void
 ArtistBrowserPage::OpenArtistList(struct mpdclient &c)
 {
-       FreeLists();
-
        mode = Mode::ARTISTS;
-       LoadArtistList(c);
+       SetCurrentPage(c, &artist_list_page);
 }
 
 void
@@ -275,23 +171,16 @@ ArtistBrowserPage::OpenArtistList(struct mpdclient &c,
 {
        OpenArtistList(c);
 
-       lw.Reset();
-
-       int idx = string_array_find(artist_list, selected_value);
-       if (idx >= 0) {
-               lw.SetCursor(idx);
-               lw.Center(idx);
-       }
+       // TODO: move cursor to selected_value
+       (void)selected_value;
 }
 
 void
 ArtistBrowserPage::OpenAlbumList(struct mpdclient &c, std::string _artist)
 {
-       FreeLists();
-
        mode = Mode::ALBUMS;
-       artist = std::move(_artist);
-       LoadAlbumList(c);
+       album_list_page.SetArtist(std::move(_artist));
+       SetCurrentPage(c, &album_list_page);
 }
 
 void
@@ -300,48 +189,18 @@ ArtistBrowserPage::OpenAlbumList(struct mpdclient &c, std::string _artist,
 {
        OpenAlbumList(c, std::move(_artist));
 
-       lw.Reset();
-
-       int idx = selected_value == nullptr
-               ? (int)album_list.size()
-               : string_array_find(album_list, selected_value);
-       if (idx >= 0) {
-               ++idx;
-               lw.SetCursor(idx);
-               lw.Center(idx);
-       }
+       // TODO: move cursor to selected_value
+       (void)selected_value;
 }
 
 void
-ArtistBrowserPage::OpenSongList(struct mpdclient &c,
-                               std::string _artist, std::string _album)
+ArtistBrowserPage::OpenSongList(struct mpdclient &c, std::string _artist,
+                               std::string _album)
 {
-       FreeLists();
-
        mode = Mode::SONGS;
-       artist = std::move(_artist);
-       album = std::move(_album);
-       LoadSongList(c);
-}
-
-void
-ArtistBrowserPage::Reload(struct mpdclient &c)
-{
-       FreeLists();
-
-       switch (mode) {
-       case Mode::ARTISTS:
-               LoadArtistList(c);
-               break;
-
-       case Mode::ALBUMS:
-               LoadAlbumList(c);
-               break;
-
-       case Mode::SONGS:
-               LoadSongList(c);
-               break;
-       }
+       song_list_page.SetArtist(std::move(_artist));
+       song_list_page.SetAlbum(std::move(_album));
+       SetCurrentPage(c, &song_list_page);
 }
 
 static Page *
@@ -350,357 +209,113 @@ screen_artist_init(ScreenManager &_screen, WINDOW *w, Size size)
        return new ArtistBrowserPage(_screen, w, size);
 }
 
-void
-ArtistBrowserPage::OnOpen(struct mpdclient &c)
-{
-       if (artist_list.empty())
-               Reload(c);
-}
-
-/**
- * Paint one item in the artist list.
- */
-static void
-paint_artist_callback(WINDOW *w, unsigned i,
-                     gcc_unused unsigned y, unsigned width,
-                     bool selected, const void *data)
-{
-       const auto &list = *(const std::vector<std::string> *)data;
-       char *p = utf8_to_locale(list[i].c_str());
-
-       screen_browser_paint_directory(w, width, selected, p);
-       g_free(p);
-}
-
-/**
- * Paint one item in the album list.  There are two virtual items
- * inserted: at the beginning, there's the special item ".." to go to
- * the parent directory, and at the end, there's the item "All tracks"
- * to view the tracks of all albums.
- */
-static void
-paint_album_callback(WINDOW *w, unsigned i,
-                    gcc_unused unsigned y, unsigned width,
-                    bool selected, const void *data)
-{
-       const auto &list = *(const std::vector<std::string> *)data;
-       const char *p;
-       char *q = nullptr;
-
-       if (i == 0)
-               p = "..";
-       else if (i == list.size() + 1)
-               p = _("All tracks");
-       else
-               p = q = utf8_to_locale(list[i - 1].c_str());
-
-       screen_browser_paint_directory(w, width, selected, p);
-       g_free(q);
-}
-
-void
-ArtistBrowserPage::Paint() const
+const char *
+SongListPage::GetTitle(char *str, size_t size) const
 {
-       switch (mode) {
-       case Mode::ARTISTS:
-               lw.Paint(paint_artist_callback, &artist_list);
-               break;
-
-       case Mode::ALBUMS:
-               lw.Paint(paint_album_callback, &album_list);
-               break;
+       char *s1 = utf8_to_locale(artist.c_str());
+
+       if (IsNulled(album))
+               g_snprintf(str, size,
+                          _("All tracks of artist: %s"), s1);
+       else if (!album.empty()) {
+               char *s2 = utf8_to_locale(album.c_str());
+               g_snprintf(str, size, _("Album: %s - %s"), s1, s2);
+               g_free(s2);
+       } else
+               g_snprintf(str, size,
+                          _("Tracks of no album of artist: %s"), s1);
+       g_free(s1);
 
-       case Mode::SONGS:
-               FileListPage::Paint();
-               break;
-       }
+       return str;
 }
 
-const char *
-ArtistBrowserPage::GetTitle(char *str, size_t size) const
+bool
+SongListPage::OnCommand(struct mpdclient &c, command_t cmd)
 {
-       switch(mode) {
-               char *s1, *s2;
+       switch(cmd) {
+       case CMD_PLAY:
+               if (lw.selected == 0) {
+                       /* handle ".." */
+                       screen.OnCommand(c, CMD_GO_PARENT_DIRECTORY);
+                       return true;
+               }
 
-       case Mode::ARTISTS:
-               g_snprintf(str, size, _("All artists"));
                break;
 
-       case Mode::ALBUMS:
-               s1 = utf8_to_locale(artist.c_str());
-               g_snprintf(str, size, _("Albums of artist: %s"), s1);
-               g_free(s1);
-               break;
+               /* continue and update... */
+       case CMD_SCREEN_UPDATE:
+               LoadSongList(c);
+               return false;
 
-       case Mode::SONGS:
-               s1 = utf8_to_locale(artist.c_str());
-
-               if (IsNulled(album))
-                       g_snprintf(str, size,
-                                  _("All tracks of artist: %s"), s1);
-               else if (!album.empty()) {
-                       s2 = utf8_to_locale(album.c_str());
-                       g_snprintf(str, size, _("Album: %s - %s"), s1, s2);
-                       g_free(s2);
-               } else
-                       g_snprintf(str, size,
-                                  _("Tracks of no album of artist: %s"), s1);
-               g_free(s1);
+       default:
                break;
        }
 
-       return str;
+       return FileListPage::OnCommand(c, cmd);
 }
 
 void
-ArtistBrowserPage::Update(struct mpdclient &c, unsigned events)
-{
-       if (filelist == nullptr)
-               return;
-
-       if (events & MPD_IDLE_DATABASE)
-               /* the db has changed -> update the list */
-               Reload(c);
-
-       if (events & (MPD_IDLE_DATABASE | MPD_IDLE_QUEUE))
-               screen_browser_sync_highlights(filelist, &c.playlist);
-
-       if (events & (MPD_IDLE_DATABASE
-#ifndef NCMPC_MINI
-                     | MPD_IDLE_QUEUE
-#endif
-                     ))
-               SetDirty();
-}
-
-/* add_query - Add all songs satisfying specified criteria.
-   _artist is actually only used in the ALBUM case to distinguish albums with
-   the same name from different artists. */
-static void
-add_query(struct mpdclient *c, enum mpd_tag_type table, const char *_filter,
-         const char *_artist)
+ArtistBrowserPage::OnOpen(struct mpdclient &c)
 {
-       struct mpd_connection *connection = mpdclient_get_connection(c);
-
-       assert(_filter != nullptr);
-
-       if (connection == nullptr)
-               return;
-
-       char *str = utf8_to_locale(_filter);
-       if (table == MPD_TAG_ALBUM)
-               screen_status_printf(_("Adding album %s..."), str);
-       else
-               screen_status_printf(_("Adding %s..."), str);
-       g_free(str);
-
-       mpd_search_db_songs(connection, true);
-       mpd_search_add_tag_constraint(connection, MPD_OPERATOR_DEFAULT,
-                                     table, _filter);
-       if (table == MPD_TAG_ALBUM)
-               mpd_search_add_tag_constraint(connection, MPD_OPERATOR_DEFAULT,
-                                             MPD_TAG_ARTIST, _artist);
-       mpd_search_commit(connection);
-
-       auto *addlist = filelist_new_recv(connection);
+       ProxyPage::OnOpen(c);
 
-       if (mpdclient_finish_command(c))
-               mpdclient_filelist_add_all(c, addlist);
-
-       delete addlist;
+       if (GetCurrentPage() == nullptr)
+               SetCurrentPage(c, &artist_list_page);
 }
 
-inline bool
-ArtistBrowserPage::OnListCommand(struct mpdclient &c, command_t cmd)
+void
+ArtistBrowserPage::Update(struct mpdclient &c, unsigned events)
 {
-       switch (mode) {
-       case Mode::ARTISTS:
-       case Mode::ALBUMS:
-               if (lw.HandleCommand(cmd)) {
-                       SetDirty();
-                       return true;
-               }
-
-               return false;
+       artist_list_page.AddPendingEvents(events);
+       album_list_page.AddPendingEvents(events);
+       song_list_page.AddPendingEvents(events);
 
-       case Mode::SONGS:
-               return FileListPage::OnCommand(c, cmd);
-       }
-
-       assert(0);
-       return 0;
+       ProxyPage::Update(c, events);
 }
 
 bool
 ArtistBrowserPage::OnCommand(struct mpdclient &c, command_t cmd)
 {
-       switch(cmd) {
-               const char *selected;
+       if (ProxyPage::OnCommand(c, cmd))
+               return true;
 
+       switch (cmd) {
        case CMD_PLAY:
-               switch (mode) {
-               case Mode::ARTISTS:
-                       if (lw.selected >= artist_list.size())
-                               return true;
-
-                       selected = artist_list[lw.selected].c_str();
-                       OpenAlbumList(c, selected);
-                       lw.Reset();
-
-                       SetDirty();
-                       return true;
-
-               case Mode::ALBUMS:
-                       if (lw.selected == 0) {
-                               /* handle ".." */
-                               OpenArtistList(c, artist.c_str());
-                       } else if (lw.selected == album_list.size() + 1) {
-                               /* handle "show all" */
-                               OpenSongList(c, std::move(artist),
-                                            MakeNulledString());
-                               lw.Reset();
-                       } else {
-                               /* select album */
-                               selected = album_list[lw.selected - 1].c_str();
-                               OpenSongList(c, std::move(artist), selected);
-                               lw.Reset();
-                       }
-
-                       SetDirty();
-                       return true;
-
-               case Mode::SONGS:
-                       if (lw.selected == 0) {
-                               /* handle ".." */
-                               OpenAlbumList(c, std::move(artist),
-                                             IsNulled(album) ? nullptr : album.c_str());
-                               SetDirty();
+               if (GetCurrentPage() == &artist_list_page) {
+                       const char *artist = artist_list_page.GetSelectedValue();
+                       if (artist != nullptr) {
+                               OpenAlbumList(c, artist);
                                return true;
                        }
-                       break;
-               }
-               break;
-
-               /* FIXME? CMD_GO_* handling duplicates code from CMD_PLAY */
-
-       case CMD_GO_PARENT_DIRECTORY:
-               switch (mode) {
-               case Mode::ARTISTS:
-                       break;
-
-               case Mode::ALBUMS:
-                       OpenArtistList(c, artist.c_str());
-                       break;
-
-               case Mode::SONGS:
-                       OpenAlbumList(c, std::move(artist),
-                                     IsNulled(album) ? nullptr : album.c_str());
-                       break;
+               } else if (GetCurrentPage() == &album_list_page) {
+                       const char *album = album_list_page.GetSelectedValue();
+                       if (album != nullptr)
+                               OpenSongList(c, album_list_page.GetArtist(),
+                                            album);
+                       else if (album_list_page.IsShowAll())
+                               OpenSongList(c, album_list_page.GetArtist(),
+                                            MakeNulledString());
                }
 
-               SetDirty();
                break;
 
        case CMD_GO_ROOT_DIRECTORY:
-               switch (mode) {
-               case Mode::ARTISTS:
-                       break;
-
-               case Mode::ALBUMS:
-               case Mode::SONGS:
-                       OpenArtistList(c);
-                       lw.Reset();
-                       /* restore first list window state (pop while returning true) */
-                       /* XXX */
-                       break;
-               }
-
-               SetDirty();
-               break;
-
-       case CMD_SELECT:
-       case CMD_ADD:
-               switch(mode) {
-               case Mode::ARTISTS:
-                       if (lw.selected >= artist_list.size())
-                               return true;
-
-                       for (const unsigned i : lw.GetRange()) {
-                               selected = artist_list[i].c_str();
-                               add_query(&c, MPD_TAG_ARTIST, selected, nullptr);
-                               cmd = CMD_LIST_NEXT; /* continue and select next item... */
-                       }
-                       break;
-
-               case Mode::ALBUMS:
-                       for (const unsigned i : lw.GetRange()) {
-                               if(i == album_list.size() + 1)
-                                       add_query(&c, MPD_TAG_ARTIST,
-                                                 artist.c_str(), nullptr);
-                               else if (i > 0)
-                               {
-                                       selected = album_list[lw.selected - 1].c_str();
-                                       add_query(&c, MPD_TAG_ALBUM, selected,
-                                                 artist.c_str());
-                                       cmd = CMD_LIST_NEXT; /* continue and select next item... */
-                               }
-                       }
-                       break;
-
-               case Mode::SONGS:
-                       /* handled by browser_cmd() */
-                       break;
-               }
-               break;
-
-               /* continue and update... */
-       case CMD_SCREEN_UPDATE:
-               Reload(c);
-               return false;
-
-       case CMD_LIST_FIND:
-       case CMD_LIST_RFIND:
-       case CMD_LIST_FIND_NEXT:
-       case CMD_LIST_RFIND_NEXT:
-               switch (mode) {
-               case Mode::ARTISTS:
-                       screen_find(screen, &lw, cmd,
-                                   screen_artist_lw_callback, &artist_list);
-                       SetDirty();
+               if (GetCurrentPage() != &artist_list_page) {
+                       OpenArtistList(c, album_list_page.GetArtist().c_str());
                        return true;
-
-               case Mode::ALBUMS:
-                       screen_find(screen, &lw, cmd,
-                                   AlbumListCallback, &album_list);
-                       SetDirty();
-                       return true;
-
-               case Mode::SONGS:
-                       /* handled by browser_cmd() */
-                       break;
                }
 
                break;
 
-       case CMD_LIST_JUMP:
-               switch (mode) {
-               case Mode::ARTISTS:
-                       screen_jump(screen, &lw,
-                                   screen_artist_lw_callback, &artist_list,
-                                   paint_artist_callback, &artist_list);
-                       SetDirty();
+       case CMD_GO_PARENT_DIRECTORY:
+               if (GetCurrentPage() == &album_list_page) {
+                       OpenArtistList(c, album_list_page.GetArtist().c_str());
                        return true;
-
-               case Mode::ALBUMS:
-                       screen_jump(screen, &lw,
-                                   AlbumListCallback, &album_list,
-                                   paint_album_callback, &album_list);
-                       SetDirty();
+               } else if (GetCurrentPage() == &song_list_page) {
+                       const auto &album = song_list_page.GetAlbum();
+                       OpenAlbumList(c, song_list_page.GetArtist(),
+                                     IsNulled(album) ? nullptr : album.c_str());
                        return true;
-
-               case Mode::SONGS:
-                       /* handled by browser_cmd() */
-                       break;
                }
 
                break;
@@ -709,9 +324,6 @@ ArtistBrowserPage::OnCommand(struct mpdclient &c, command_t cmd)
                break;
        }
 
-       if (OnListCommand(c, cmd))
-               return true;
-
        return false;
 }