diff --git a/src/actions.cpp b/src/actions.cpp index 6fc22609..6be3303e 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -778,48 +778,17 @@ void Delete::Run() if (!myPlaylist->Items->empty() && myScreen == myPlaylist) { - if (myPlaylist->Items->hasSelected()) + auto list = getSelectedOrCurrent(myPlaylist->Items->begin(), myPlaylist->Items->end(), myPlaylist->Items->currentI()); + Mpd.StartCommandsList(); + for (auto it = list.rbegin(); it != list.rend(); ++it) + Mpd.DeleteID((*it)->value().getID()); + if (Mpd.CommitCommandsList()) { - std::vector list; - myPlaylist->Items->getSelected(list); - Mpd.StartCommandsList(); - for (std::vector::reverse_iterator it = list.rbegin(); it != list.rend(); ++it) - Mpd.DeleteID((*myPlaylist->Items)[*it].value().getID()); - if (Mpd.CommitCommandsList()) - { - for (auto it = myPlaylist->Items->begin(); it != myPlaylist->Items->end(); ++it) - it->setSelected(false); + for (auto it = list.begin(); it != list.end(); ++it) + (*it)->setSelected(false); + if (list.size() > 1) ShowMessage("Selected items deleted"); - } } - else - Mpd.DeleteID(myPlaylist->currentSong()->getID()); - } - else if ( - (myScreen == myBrowser && !myBrowser->Main()->empty() && myBrowser->CurrentDir() == "/" && myBrowser->Main()->current().value().type == itPlaylist) - || (myScreen->ActiveWindow() == myPlaylistEditor->Playlists) - ) - { - std::string name; - if (myScreen == myBrowser) - name = myBrowser->Main()->current().value().name; - else - name = myPlaylistEditor->Playlists->current().value(); - bool yes = AskYesNoQuestion("Delete playlist \"" + Shorten(TO_WSTRING(name), COLS-28) + "\"?", TraceMpdStatus); - if (yes) - { - if (Mpd.DeletePlaylist(locale_to_utf_cpy(name))) - { - const char msg[] = "Playlist \"%s\" deleted"; - ShowMessage(msg, Shorten(TO_WSTRING(name), COLS-const_strlen(msg)).c_str()); - if (myBrowser->Main() && !myBrowser->isLocal() && myBrowser->CurrentDir() == "/") - myBrowser->GetDirectory("/"); - } - } - else - ShowMessage("Aborted"); - if (myPlaylistEditor->Main()) // check if initialized - myPlaylistEditor->Playlists->clear(); // make playlists list update itself } # ifndef WIN32 else if (myScreen == myBrowser && !myBrowser->Main()->empty()) @@ -827,46 +796,29 @@ void Delete::Run() if (!myBrowser->isLocal() && !isMPDMusicDirSet()) return; - MPD::Item &item = myBrowser->Main()->current().value(); - - if (item.type == itSong && !Config.allow_physical_files_deletion) - { - ShowMessage("Deleting files is disabled by default, see man page for more details"); - return; - } - if (item.type == itDirectory && !Config.allow_physical_directories_deletion) - { - ShowMessage("Deleting directories is disabled by default, see man page for more details"); - return; - } - if (myBrowser->isParentDirectory(item)) - return; - - std::string name = item.type == itSong ? item.song->getName() : item.name; std::string question; if (myBrowser->Main()->hasSelected()) question = "Delete selected items?"; else { + MPD::Item &item = myBrowser->Main()->current().value(); + std::string name = item.type == itSong ? item.song->getName() : item.name; question = "Delete "; - question += (item.type == itSong ? "file" : item.type == itDirectory ? "directory" : "playlist"); + question += itemTypeToString(item.type); question += " \""; - question += Shorten(TO_WSTRING(name), COLS-30); + question += Shorten(TO_WSTRING(name), COLS-question.size()-10); question += "\"?"; } bool yes = AskYesNoQuestion(question, TraceMpdStatus); if (yes) { - std::vector list; - myBrowser->Main()->getSelected(list); - if (list.empty()) - list.push_back(myBrowser->Main()->choice()); - bool success = 1; - for (size_t i = 0; i < list.size(); ++i) + bool success = true; + auto list = getSelectedOrCurrent(myBrowser->Main()->begin(), myBrowser->Main()->end(), myBrowser->Main()->currentI()); + for (auto it = list.begin(); it != list.end(); ++it) { - const MPD::Item &it = (*myBrowser->Main())[list[i]].value(); - name = it.type == itSong ? it.song->getName() : it.name; - if (myBrowser->deleteItem(it)) + const MPD::Item &i = (*it)->value(); + std::string name = i.type == itSong ? i.song->getName() : i.name; + if (myBrowser->deleteItem(i)) { const char msg[] = "\"%s\" deleted"; ShowMessage(msg, Shorten(TO_WSTRING(name), COLS-const_strlen(msg)).c_str()); @@ -875,7 +827,7 @@ void Delete::Run() { const char msg[] = "Couldn't delete \"%s\": %s"; ShowMessage(msg, Shorten(TO_WSTRING(name), COLS-const_strlen(msg)-25).c_str(), strerror(errno)); - success = 0; + success = false; break; } } @@ -883,8 +835,6 @@ void Delete::Run() { if (!myBrowser->isLocal()) Mpd.UpdateDirectory(myBrowser->CurrentDir()); - else - myBrowser->GetDirectory(myBrowser->CurrentDir()); } } else @@ -892,27 +842,47 @@ void Delete::Run() } # endif // !WIN32 - else if (myScreen->ActiveWindow() == myPlaylistEditor->Content && !myPlaylistEditor->Content->empty()) + else if (myScreen == myPlaylistEditor && !myPlaylistEditor->Content->empty()) { - if (myPlaylistEditor->Content->hasSelected()) + if (myScreen->ActiveWindow() == myPlaylistEditor->Playlists) { - std::vector list; - myPlaylistEditor->Content->getSelected(list); - std::string playlist = locale_to_utf_cpy(myPlaylistEditor->Playlists->current().value()); - ShowMessage("Deleting selected items..."); - Mpd.StartCommandsList(); - for (std::vector::reverse_iterator it = list.rbegin(); it != list.rend(); ++it) + std::string question; + if (myPlaylistEditor->Playlists->hasSelected()) + question = "Delete selected playlists?"; + else { - Mpd.Delete(playlist, *it); - myPlaylistEditor->Content->deleteItem(*it); + question = "Delete playlist \""; + question += Shorten(TO_WSTRING(myPlaylistEditor->Playlists->current().value()), COLS-question.size()-10); + question += "\"?"; } - Mpd.CommitCommandsList(); - ShowMessage("Selected items deleted from playlist \"%s\"", myPlaylistEditor->Playlists->current().value().c_str()); + bool yes = AskYesNoQuestion(question, TraceMpdStatus); + if (yes) + { + auto list = getSelectedOrCurrent(myPlaylistEditor->Playlists->begin(), myPlaylistEditor->Playlists->end(), myPlaylistEditor->Playlists->currentI()); + Mpd.StartCommandsList(); + for (auto it = list.begin(); it != list.end(); ++it) + Mpd.DeletePlaylist((*it)->value()); + if (Mpd.CommitCommandsList()) + ShowMessage("Playlist%s deleted", list.size() == 1 ? "" : "s"); + } + else + ShowMessage("Aborted"); } - else + else if (myScreen->ActiveWindow() == myPlaylistEditor->Content) { - if (Mpd.Delete(myPlaylistEditor->Playlists->current().value(), myPlaylistEditor->Content->choice())) - myPlaylistEditor->Content->deleteItem(myPlaylistEditor->Content->choice()); + // select current song so we won't lost track of it after filtering is canceled + selectCurrentIfNoneSelected(*myPlaylistEditor->Content); + myPlaylistEditor->Content->showAll(); + auto list = getSelected(myPlaylistEditor->Content->begin(), myPlaylistEditor->Content->end()); + Mpd.StartCommandsList(); + auto begin = myPlaylistEditor->Content->begin(); + for (auto it = list.rbegin(); it != list.rend(); ++it) + Mpd.Delete(myPlaylistEditor->Playlists->current().value(), *it-begin); + if (Mpd.CommitCommandsList()) + { + if (list.size() > 1) + ShowMessage("Selected items deleted"); + } } } } @@ -1052,56 +1022,18 @@ void MoveSelectedItemsDown::Run() myPlaylistEditor->MoveSelectedItems(Playlist::mDown); } +bool MoveSelectedItemsTo::canBeRun() const +{ + return myScreen->ActiveWindow() == myPlaylist->Items + || myScreen->ActiveWindow() == myPlaylistEditor->Content; +} + void MoveSelectedItemsTo::Run() { - if (myPlaylist->isFiltered()) - return; - if (!myPlaylist->Items->hasSelected()) - { - ShowMessage("No selected items to move"); - return; - } - size_t pos = myPlaylist->Items->choice(); - std::vector list; - myPlaylist->Items->getSelected(list); - if (pos >= list.front() && pos <= list.back()) // can't move to the middle of selected items - return; - ++pos; // always move after currently highlighted item - int diff = pos-list.front(); - Mpd.StartCommandsList(); - if (diff > 0) - { - pos -= list.size(); - size_t i = list.size()-1; - std::vector::reverse_iterator it = list.rbegin(); - for (; it != list.rend(); ++it, --i) - Mpd.Move(*it, pos+i); - if (Mpd.CommitCommandsList()) - { - i = list.size()-1; - for (it = list.rbegin(); it != list.rend(); ++it, --i) - { - myPlaylist->Items->at(*it).setSelected(false); - myPlaylist->Items->at(pos+i).setSelected(true); - } - } - } - else if (diff < 0) - { - size_t i = 0; - std::vector::const_iterator it = list.begin(); - for (; it != list.end(); ++it, ++i) - Mpd.Move(*it, pos+i); - if (Mpd.CommitCommandsList()) - { - i = 0; - for (it = list.begin(); it != list.end(); ++it, ++i) - { - myPlaylist->Items->at(*it).setSelected(false); - myPlaylist->Items->at(pos+i).setSelected(true); - } - } - } + if (myScreen == myPlaylist) + moveSelectedItemsTo(*myPlaylist->Items, std::bind(&MPD::Connection::Move, _1, _2, _3)); + else + moveSelectedItemsTo(*myPlaylistEditor->Content, std::bind(&MPD::Connection::PlaylistMove, _1, myPlaylistEditor->Playlists->current().value(), _2, _3)); } bool Add::canBeRun() const diff --git a/src/actions.h b/src/actions.h index 2b6118f4..724866ef 100644 --- a/src/actions.h +++ b/src/actions.h @@ -355,6 +355,7 @@ struct MoveSelectedItemsDown : public Action struct MoveSelectedItemsTo : public Action { MoveSelectedItemsTo() : Action(aMoveSelectedItemsTo, "move_selected_items_to") { } + virtual bool canBeRun() const; virtual void Run(); }; diff --git a/src/helpers.h b/src/helpers.h index 295a5d82..cc6e7d54 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -50,6 +50,189 @@ inline MPD::Song *currentSong(BasicScreen *screen) return ptr; } +template bool hasSelected(Iterator first, Iterator last) +{ + for (; first != last; ++first) + if (first->isSelected()) + return true; + return false; +} + +template std::vector getSelected(Iterator first, Iterator last) +{ + std::vector result; + for (; first != last; ++first) + if (first->isSelected()) + result.push_back(first); + return result; +} + +template void selectCurrentIfNoneSelected(NC::Menu &m) +{ + if (!hasSelected(m.begin(), m.end())) + m.current().setSelected(true); +} + +template +std::vector getSelectedOrCurrent(Iterator first, Iterator last, Iterator current) +{ + std::vector result = getSelected(first, last); + if (result.empty()) + result.push_back(current); + return result; +} + +template void withUnfilteredMenu(NC::Menu &m, F action) +{ + bool is_filtered = m.isFiltered(); + m.showAll(); + action(); + if (is_filtered) + m.showFiltered(); +} + +template +void withUnfilteredMenuReapplyFilter(NC::Menu &m, F action) +{ + m.showAll(); + action(); + if (m.getFilter()) + { + m.applyCurrentFilter(m.begin(), m.end()); + if (m.empty()) + { + m.clearFilter(); + m.clearFilterResults(); + } + } +} + +template +void moveSelectedItemsUp(NC::Menu &m, F swap_fun) +{ + if (m.choice() > 0) + selectCurrentIfNoneSelected(m); + auto list = getSelected(m.begin(), m.end()); + auto begin = m.begin(); + if (!list.empty() && list.front() != m.begin()) + { + Mpd.StartCommandsList(); + for (auto it = list.begin(); it != list.end(); ++it) + swap_fun(Mpd, *it - begin, *it - begin - 1); + if (Mpd.CommitCommandsList()) + { + if (list.size() > 1) + { + for (auto it = list.begin(); it != list.end(); ++it) + { + (*it)->setSelected(false); + (*it-1)->setSelected(true); + } + m.highlight(list[(list.size())/2] - begin - 1); + } + else + { + // if we move only one item, do not select it. however, if single item + // was selected prior to move, it'll deselect it. oh well. + list[0]->setSelected(false); + m.scroll(NC::wUp); + } + } + } +} + +template +void moveSelectedItemsDown(NC::Menu &m, F swap_fun) +{ + if (m.choice() < m.size()-1) + selectCurrentIfNoneSelected(m); + auto list = getSelected(m.rbegin(), m.rend()); + auto begin = m.begin() + 1; // reverse iterators add 1, so we need to cancel it + if (!list.empty() && list.front() != m.rbegin()) + { + Mpd.StartCommandsList(); + for (auto it = list.begin(); it != list.end(); ++it) + swap_fun(Mpd, it->base() - begin, it->base() - begin + 1); + if (Mpd.CommitCommandsList()) + { + if (list.size() > 1) + { + for (auto it = list.begin(); it != list.end(); ++it) + { + (*it)->setSelected(false); + (*it-1)->setSelected(true); + } + m.highlight(list[(list.size())/2].base() - begin + 1); + } + else + { + // if we move only one item, do not select it. however, if single item + // was selected prior to move, it'll deselect it. oh well. + list[0]->setSelected(false); + m.scroll(NC::wDown); + } + } + } +} + +template +void moveSelectedItemsTo(NC::Menu &m, F move_fun) +{ + if (m.empty()) + return; + auto cur_ptr = &m.current().value(); + withUnfilteredMenu(m, [&]() { + // this is kinda shitty, but there is no other way to know + // what position current item has in unfiltered menu. + ptrdiff_t pos = 0; + for (auto it = m.begin(); it != m.end(); ++it, ++pos) + if (&it->value() == cur_ptr) + break; + auto begin = m.begin(); + auto list = getSelected(m.begin(), m.end()); + // we move only truly selected items + if (list.empty()) + return; + // we can't move to the middle of selected items + //(this also handles case when list.size() == 1) + if (pos >= (list.front() - begin) && pos <= (list.back() - begin)) + return; + int diff = pos - (list.front() - begin); + Mpd.StartCommandsList(); + if (diff > 0) // move down + { + pos -= list.size(); + size_t i = list.size()-1; + for (auto it = list.rbegin(); it != list.rend(); ++it, --i) + move_fun(Mpd, *it - begin, pos+i); + if (Mpd.CommitCommandsList()) + { + i = list.size()-1; + for (auto it = list.rbegin(); it != list.rend(); ++it, --i) + { + (*it)->setSelected(false); + m[pos+i].setSelected(true); + } + } + } + else if (diff < 0) // move up + { + size_t i = 0; + for (auto it = list.begin(); it != list.end(); ++it, ++i) + move_fun(Mpd, *it - begin, pos+i); + if (Mpd.CommitCommandsList()) + { + i = 0; + for (auto it = list.begin(); it != list.end(); ++it, ++i) + { + (*it)->setSelected(false); + m[pos+i].setSelected(true); + } + } + } + }); +} + template void reverseSelectionHelper(Iterator first, Iterator last) { for (; first != last; ++first) @@ -69,15 +252,6 @@ template std::string getSharedDirectory(Iterator first, Iter return result; } -template void withUnfilteredMenu(NC::Menu &menu, std::function action) -{ - bool is_filtered = menu.isFiltered(); - menu.showAll(); - action(); - if (is_filtered) - menu.showFiltered(); -} - void ParseArgv(int, char **); template struct StringConverter { diff --git a/src/menu.h b/src/menu.h index 2f9504c1..884b0313 100644 --- a/src/menu.h +++ b/src/menu.h @@ -60,6 +60,10 @@ template struct Menu : public Window bool isSeparator() const { return m_is_separator; } private: + // make those private, they shouldn't be used + Item(const Item &) { assert(false); } + Item &operator=(const Item &) { assert(false); } + static Item *mkSeparator() { Item *i = new Item; @@ -234,10 +238,6 @@ template struct Menu : public Window /// @return true if it contains them, false otherwise bool hasSelected() const; - /// Gets positions of items that are selected - /// @param v vector to be filled with selected positions numbers - void getSelected(std::vector &v) const; - /// Highlights given position /// @param pos position to be highlighted void highlight(size_t pos); @@ -247,11 +247,15 @@ template struct Menu : public Window void filter(ConstIterator first, ConstIterator last, const FilterFunction &f); + void applyCurrentFilter(ConstIterator first, ConstIterator last); + bool search(ConstIterator first, ConstIterator last, const FilterFunction &f); /// Clears filter results void clearFilterResults(); + void clearFilter(); + /// Clears search results void clearSearchResults(); @@ -378,6 +382,11 @@ template struct Menu : public Window /// @return const reference to item at given position Menu::Item &operator[](size_t pos) { return *(*m_options_ptr)[pos]; } + Iterator currentI() { return Iterator(m_options_ptr->begin() + m_highlight); } + ConstIterator currentI() const { return ConstIterator(m_options_ptr->begin() + m_highlight); } + ValueIterator currentVI() { return ValueIterator(m_options_ptr->begin() + m_highlight); } + ConstValueIterator currentVI() const { return ConstValueIterator(m_options_ptr->begin() + m_highlight); } + Iterator begin() { return Iterator(m_options_ptr->begin()); } ConstIterator begin() const { return ConstIterator(m_options_ptr->begin()); } Iterator end() { return Iterator(m_options_ptr->end()); } @@ -412,7 +421,6 @@ private: std::vector *m_options_ptr; std::vector m_options; std::vector m_filtered_options; - std::vector m_filtered_positions; std::set m_found_positions; size_t m_beginning; @@ -697,13 +705,6 @@ template bool Menu::hasSelected() const return false; } -template void Menu::getSelected(std::vector &v) const -{ - for (size_t i = 0; i < m_options_ptr->size(); ++i) - if ((*m_options_ptr)[i]->isSelected()) - v.push_back(i); -} - template void Menu::highlight(size_t pos) { m_highlight = pos; @@ -727,24 +728,33 @@ void Menu::filter(ConstIterator first, ConstIterator last, const FilterFuncti clearFilterResults(); m_filter = f; for (auto it = first; it != last; ++it) - { if (m_filter(*it)) - { - size_t pos = it-begin(); - m_filtered_positions.push_back(pos); m_filtered_options.push_back(*it.base()); - } - } - m_options_ptr = &m_filtered_options; + if (m_filtered_options == m_options) + m_filtered_options.clear(); + else + m_options_ptr = &m_filtered_options; } +template +void Menu::applyCurrentFilter(ConstIterator first, ConstIterator last) +{ + assert(m_filter); + filter(first, last, m_filter); +} + + template void Menu::clearFilterResults() { m_filtered_options.clear(); - m_filtered_positions.clear(); m_options_ptr = &m_options; } +template void Menu::clearFilter() +{ + m_filter = 0; +} + template bool Menu::search(ConstIterator first, ConstIterator last, const FilterFunction &f) { diff --git a/src/mpdpp.cpp b/src/mpdpp.cpp index e8104004..3157e4bc 100644 --- a/src/mpdpp.cpp +++ b/src/mpdpp.cpp @@ -241,6 +241,8 @@ void MPD::Connection::UpdateStatus() { if (idle_mask != 0) { + itsChanges.Playlist = idle_mask & MPD_IDLE_QUEUE; + itsChanges.StoredPlaylists = idle_mask & MPD_IDLE_STORED_PLAYLIST; itsChanges.Database = idle_mask & MPD_IDLE_DATABASE; itsChanges.DBUpdating = idle_mask & MPD_IDLE_UPDATE; itsChanges.Volume = idle_mask & MPD_IDLE_MIXER; @@ -249,6 +251,9 @@ void MPD::Connection::UpdateStatus() } else { + itsChanges.Playlist = mpd_status_get_queue_version(itsOldStatus) + != mpd_status_get_queue_version(itsCurrentStatus); + itsChanges.ElapsedTime = mpd_status_get_elapsed_time(itsOldStatus) != mpd_status_get_elapsed_time(itsCurrentStatus); @@ -273,9 +278,6 @@ void MPD::Connection::UpdateStatus() itsChanges.Outputs = 0; } - itsChanges.Playlist = mpd_status_get_queue_version(itsOldStatus) - != mpd_status_get_queue_version(itsCurrentStatus); - itsChanges.SongID = mpd_status_get_song_id(itsOldStatus) != mpd_status_get_song_id(itsCurrentStatus); @@ -588,7 +590,7 @@ void MPD::Connection::AddToPlaylist(const std::string &path, const std::string & } } -bool MPD::Connection::Move(const std::string &path, int from, int to) +bool MPD::Connection::PlaylistMove(const std::string &path, int from, int to) { if (!itsConnection) return false; diff --git a/src/mpdpp.h b/src/mpdpp.h index 0bd8278b..bbf3403b 100644 --- a/src/mpdpp.h +++ b/src/mpdpp.h @@ -42,8 +42,9 @@ namespace MPD struct StatusChanges { - StatusChanges() : Playlist(0), SongID(0), Database(0), DBUpdating(0), Volume(0), ElapsedTime(0), Crossfade(0), Random(0), Repeat(0), Single(0), Consume(0), PlayerState(0), StatusFlags(0), Outputs(0) { } + StatusChanges() : Playlist(0), StoredPlaylists(0), SongID(0), Database(0), DBUpdating(0), Volume(0), ElapsedTime(0), Crossfade(0), Random(0), Repeat(0), Single(0), Consume(0), PlayerState(0), StatusFlags(0), Outputs(0) { } bool Playlist:1; + bool StoredPlaylists:1; bool SongID:1; bool Database:1; bool DBUpdating:1; @@ -193,7 +194,7 @@ namespace MPD bool ClearPlaylist(const std::string &); void AddToPlaylist(const std::string &, const Song &); void AddToPlaylist(const std::string &, const std::string &); - bool Move(const std::string &, int, int); + bool PlaylistMove(const std::string &, int, int); bool Rename(const std::string &, const std::string &); void StartSearch(bool); diff --git a/src/playlist.cpp b/src/playlist.cpp index 22111e52..db9b1e50 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -167,14 +167,14 @@ void Playlist::EnterPressed() { size_t pos = SortDialog->choice(); - size_t beginning = 0; - size_t end = Items->size(); + auto begin = Items->begin(), end = Items->end(); + // if songs are selected, sort range from first selected to last selected if (Items->hasSelected()) { - std::vector list; - Items->getSelected(list); - beginning = *list.begin(); - end = *list.rbegin()+1; + while (!begin->isSelected()) + ++begin; + while (!(end-1)->isSelected()) + --end; } if (pos > SortOptions) @@ -191,10 +191,11 @@ void Playlist::EnterPressed() return; } + size_t start_pos = begin-Items->begin(); MPD::SongList playlist; - playlist.reserve(end-beginning); - for (size_t i = beginning; i < end; ++i) - playlist.push_back((*Items)[i].value()); + playlist.reserve(end-begin); + for (; begin != end; ++begin) + playlist.push_back(begin->value()); std::function iter_swap, quick_sort; auto song_cmp = [](const MPD::Song &a, const MPD::Song &b) -> bool { @@ -204,9 +205,9 @@ void Playlist::EnterPressed() return ret < 0; return a.getPosition() < b.getPosition(); }; - iter_swap = [&playlist](MPD::SongList::iterator a, MPD::SongList::iterator b) { + iter_swap = [&playlist, &start_pos](MPD::SongList::iterator a, MPD::SongList::iterator b) { std::iter_swap(a, b); - Mpd.Swap(a-playlist.begin(), b-playlist.begin()); + Mpd.Swap(start_pos+a-playlist.begin(), start_pos+b-playlist.begin()); }; quick_sort = [this, &song_cmp, &quick_sort, &iter_swap](MPD::SongList::iterator first, MPD::SongList::iterator last) { if (last-first > 1) @@ -398,65 +399,12 @@ void Playlist::MoveSelectedItems(Movement where) { case mUp: { - if (Items->hasSelected()) - { - std::vector list; - myPlaylist->Items->getSelected(list); - if (list.front() > 0) - { - Mpd.StartCommandsList(); - std::vector::const_iterator it = list.begin(); - for (; it != list.end(); ++it) - Mpd.Move(*it-1, *it); - if (Mpd.CommitCommandsList()) - { - Items->at(list.back()).setSelected(false); - Items->at(list.front()-1).setSelected(true); - Items->highlight(list[(list.size()-1)/2]-1); - } - } - } - else - { - size_t pos = myPlaylist->Items->choice(); - if (pos > 0) - { - if (Mpd.Move(pos-1, pos)) - Items->scroll(NC::wUp); - } - } + moveSelectedItemsUp(*Items, std::bind(&MPD::Connection::Move, _1, _2, _3)); break; } case mDown: { - if (Items->hasSelected()) - { - std::vector list; - Items->getSelected(list); - - if (list.back() < Items->size()-1) - { - Mpd.StartCommandsList(); - std::vector::const_reverse_iterator it = list.rbegin(); - for (; it != list.rend(); ++it) - Mpd.Move(*it, *it+1); - if (Mpd.CommitCommandsList()) - { - Items->at(list.front()).setSelected(false); - Items->at(list.back()+1).setSelected(true); - Items->highlight(list[(list.size()-1)/2]+1); - } - } - } - else - { - size_t pos = Items->choice(); - if (pos < Items->size()-1) - { - if (Mpd.Move(pos, pos+1)) - Items->scroll(NC::wDown); - } - } + moveSelectedItemsDown(*Items, std::bind(&MPD::Connection::Move, _1, _2, _3)); break; } } @@ -675,13 +623,10 @@ void Playlist::PlayNewlyAddedSongs() void Playlist::SetSelectedItemsPriority(int prio) { - std::vector list; - myPlaylist->Items->getSelected(list); - if (list.empty()) - list.push_back(Items->choice()); + auto list = getSelectedOrCurrent(Items->begin(), Items->end(), Items->currentI()); Mpd.StartCommandsList(); - for (std::vector::const_iterator it = list.begin(); it != list.end(); ++it) - Mpd.SetPriority((*Items)[*it].value(), prio); + for (auto it = list.begin(); it != list.end(); ++it) + Mpd.SetPriority((*it)->value(), prio); if (Mpd.CommitCommandsList()) ShowMessage("Priority set"); } diff --git a/src/playlist_editor.cpp b/src/playlist_editor.cpp index d306d685..7694c4a6 100644 --- a/src/playlist_editor.cpp +++ b/src/playlist_editor.cpp @@ -133,46 +133,64 @@ void PlaylistEditor::SwitchTo() Global::myPrevScreen = myScreen; myScreen = this; Global::RedrawHeader = true; - Refresh(); UpdateSongList(Content); + Refresh(); } void PlaylistEditor::Update() { - if (Playlists->reallyEmpty()) + if (Playlists->reallyEmpty() || playlistsUpdateRequested) { - Content->clear(); - auto list = Mpd.GetPlaylists(); - std::sort(list.begin(), list.end(), CaseInsensitiveSorting()); - for (auto it = list.begin(); it != list.end(); ++it) - Playlists->addItem(*it); + playlistsUpdateRequested = false; + Playlists->clearSearchResults(); + withUnfilteredMenuReapplyFilter(*Playlists, [this]() { + auto list = Mpd.GetPlaylists(); + std::sort(list.begin(), list.end(), CaseInsensitiveSorting()); + auto playlist = list.begin(); + if (Playlists->size() > list.size()) + Playlists->resizeList(list.size()); + for (auto it = Playlists->begin(); it != Playlists->end(); ++it, ++playlist) + it->value() = *playlist; + for (; playlist != list.end(); ++playlist) + Playlists->addItem(*playlist); + }); Playlists->refresh(); } - if (!Playlists->empty() && Content->reallyEmpty()) + if (!Playlists->empty() && (Content->reallyEmpty() || contentUpdateRequested)) { - Content->reset(); - size_t plsize = 0; - auto songs = Mpd.GetPlaylistContent(Playlists->current().value()); - for (auto s = songs.begin(); s != songs.end(); ++s, ++plsize) - Content->addItem(*s, myPlaylist->checkForSong(*s)); - std::string title; - if (Config.titles_visibility) - { - title = "Playlist content"; - if (plsize > 0) + contentUpdateRequested = false; + Content->clearSearchResults(); + withUnfilteredMenuReapplyFilter(*Content, [this]() { + auto list = Mpd.GetPlaylistContent(Playlists->current().value()); + auto song = list.begin(); + if (Content->size() > list.size()) + Content->resizeList(list.size()); + for (auto it = Content->begin(); it != Content->end(); ++it, ++song) { - title += " ("; - title += unsignedLongIntTo::apply(plsize); - title += " item"; - if (plsize == 1) - title += ")"; - else - title += "s)"; + it->value() = *song; + it->setBold(myPlaylist->checkForSong(*song)); } - title.resize(Content->getWidth()); - } - Content->setTitle(title); + for (; song != list.end(); ++song) + Content->addItem(*song, myPlaylist->checkForSong(*song)); + std::string title; + if (Config.titles_visibility) + { + title = "Playlist content"; + if (list.size() > 0) + { + title += " ("; + title += unsignedLongIntTo::apply(list.size()); + title += " item"; + if (list.size() == 1) + title += ")"; + else + title += "s)"; + } + title.resize(Content->getWidth()); + } + Content->setTitle(title); + }); Content->display(); } @@ -192,83 +210,19 @@ void PlaylistEditor::Update() void PlaylistEditor::MoveSelectedItems(Playlist::Movement where) { - if (Content->empty()) + if (Content->empty() || isContentFiltered()) return; - // remove search results as we may move them to different positions, but - // search rememebers positions and may point to wrong ones after that. - Content->clearSearchResults(); - switch (where) { case Playlist::mUp: { - if (Content->hasSelected()) - { - std::vector list; - Content->getSelected(list); - - if (list.front() > 0) - { - Mpd.StartCommandsList(); - std::vector::const_iterator it = list.begin(); - for (; it != list.end(); ++it) - Mpd.Move(Playlists->current().value(), *it-1, *it); - if (Mpd.CommitCommandsList()) - { - for (it = list.begin(); it != list.end(); ++it) - Content->Swap(*it-1, *it); - Content->highlight(list[(list.size()-1)/2]-1); - } - } - } - else - { - size_t pos = Content->choice(); - if (pos > 0) - { - if (Mpd.Move(Playlists->current().value(), pos-1, pos)) - { - Content->scroll(NC::wUp); - Content->Swap(pos-1, pos); - } - } - } + moveSelectedItemsUp(*Content, std::bind(&MPD::Connection::PlaylistMove, _1, Playlists->current().value(), _2, _3)); break; } case Playlist::mDown: { - if (Content->hasSelected()) - { - std::vector list; - Content->getSelected(list); - - if (list.back() < Content->size()-1) - { - Mpd.StartCommandsList(); - std::vector::const_reverse_iterator it = list.rbegin(); - for (; it != list.rend(); ++it) - Mpd.Move(Playlists->current().value(), *it, *it+1); - if (Mpd.CommitCommandsList()) - { - Content->highlight(list[(list.size()-1)/2]+1); - for (it = list.rbegin(); it != list.rend(); ++it) - Content->Swap(*it, *it+1); - } - } - } - else - { - size_t pos = Content->choice(); - if (pos < Content->size()-1) - { - if (Mpd.Move(Playlists->current().value(), pos, pos+1)) - { - Content->scroll(NC::wDown); - Content->Swap(pos, pos+1); - } - } - } + moveSelectedItemsDown(*Content, std::bind(&MPD::Connection::PlaylistMove, _1, Playlists->current().value(), _2, _3)); break; } } diff --git a/src/playlist_editor.h b/src/playlist_editor.h index 9c84480c..05c401a2 100644 --- a/src/playlist_editor.h +++ b/src/playlist_editor.h @@ -63,6 +63,9 @@ class PlaylistEditor : public Screen, public Filterable, public HasS void MoveSelectedItems(Playlist::Movement where); + void requestPlaylistsUpdate() { playlistsUpdateRequested = true; } + void requestContentsUpdate() { contentUpdateRequested = true; } + bool isContentFiltered(); bool isNextColumnAvailable(); bool NextColumn(); @@ -78,6 +81,9 @@ class PlaylistEditor : public Screen, public Filterable, public HasS private: void AddToPlaylist(bool); + + bool playlistsUpdateRequested; + bool contentUpdateRequested; }; extern PlaylistEditor *myPlaylistEditor; diff --git a/src/status.cpp b/src/status.cpp index b05cc878..649c1973 100644 --- a/src/status.cpp +++ b/src/status.cpp @@ -228,52 +228,37 @@ void NcmpcppStatusChanged(MPD::Connection *, MPD::StatusChanges changed, void *) WindowTitle(np.toString(Config.song_window_title_format)); myPlaylist->Items->clearSearchResults(); - - bool is_filtered = myPlaylist->Items->isFiltered(); - myPlaylist->Items->showAll(); - - size_t playlist_length = Mpd.GetPlaylistLength(); - if (playlist_length < myPlaylist->Items->size()) - { - auto it = myPlaylist->Items->begin()+playlist_length; - auto end = myPlaylist->Items->end(); - for (; it != end; ++it) - myPlaylist->unregisterHash(it->value().getHash()); - myPlaylist->Items->resizeList(playlist_length); - } - - auto songs = Mpd.GetPlaylistChanges(Mpd.GetOldPlaylistID()); - for (auto s = songs.begin(); s != songs.end(); ++s) - { - size_t pos = s->getPosition(); - if (pos < myPlaylist->Items->size()) + withUnfilteredMenuReapplyFilter(*myPlaylist->Items, []() { + size_t playlist_length = Mpd.GetPlaylistLength(); + if (playlist_length < myPlaylist->Items->size()) { - // if song's already in playlist, replace it with a new one - MPD::Song &old_s = myPlaylist->Items->at(pos).value(); - myPlaylist->unregisterHash(old_s.getHash()); - old_s = *s; + auto it = myPlaylist->Items->begin()+playlist_length; + auto end = myPlaylist->Items->end(); + for (; it != end; ++it) + myPlaylist->unregisterHash(it->value().getHash()); + myPlaylist->Items->resizeList(playlist_length); } - else + + auto songs = Mpd.GetPlaylistChanges(Mpd.GetOldPlaylistID()); + for (auto s = songs.begin(); s != songs.end(); ++s) { - // otherwise just add it to playlist - myPlaylist->Items->addItem(*s); + size_t pos = s->getPosition(); + if (pos < myPlaylist->Items->size()) + { + // if song's already in playlist, replace it with a new one + MPD::Song &old_s = (*myPlaylist->Items)[pos].value(); + myPlaylist->unregisterHash(old_s.getHash()); + old_s = *s; + } + else // otherwise just add it to playlist + myPlaylist->Items->addItem(*s); + myPlaylist->registerHash(s->getHash()); } - myPlaylist->registerHash(s->getHash()); - } - - if (is_filtered) - { - myPlaylist->applyFilter(myPlaylist->currentFilter()); - if (myPlaylist->Items->empty()) - myPlaylist->Items->showAll(); - } + }); Playlist::ReloadTotalLength = true; Playlist::ReloadRemaining = true; - if (myPlaylist->Items->empty()) - myPlaylist->Items->reset(); - if (isVisible(myBrowser)) myBrowser->UpdateItemList(); if (isVisible(mySearcher)) @@ -283,6 +268,20 @@ void NcmpcppStatusChanged(MPD::Connection *, MPD::StatusChanges changed, void *) if (isVisible(myPlaylistEditor)) UpdateSongList(myPlaylistEditor->Content); } + if (changed.StoredPlaylists) + { + if (myPlaylistEditor->Main()) + { + myPlaylistEditor->requestPlaylistsUpdate(); + myPlaylistEditor->requestContentsUpdate(); + } + if (myBrowser->Main() && myBrowser->CurrentDir() == "/") + { + myBrowser->GetDirectory("/"); + if (isVisible(myBrowser)) + myBrowser->Refresh(); + } + } if (changed.Database) { if (myBrowser->Main()) diff --git a/src/tag_editor.cpp b/src/tag_editor.cpp index 2949479f..887ff698 100644 --- a/src/tag_editor.cpp +++ b/src/tag_editor.cpp @@ -526,14 +526,15 @@ void TagEditor::EnterPressed() EditedSongs.clear(); if (Tags->hasSelected()) // if there are selected songs, perform operations only on them { - std::vector selected; - Tags->getSelected(selected); - for (auto it = selected.begin(); it != selected.end(); ++it) - EditedSongs.push_back(&(*Tags)[*it].value()); + for (auto it = Tags->begin(); it != Tags->end(); ++it) + if (it->isSelected()) + EditedSongs.push_back(&it->value()); } else - for (size_t i = 0; i < Tags->size(); ++i) - EditedSongs.push_back(&(*Tags)[i].value()); + { + for (auto it = Tags->begin(); it != Tags->end(); ++it) + EditedSongs.push_back(&it->value()); + } size_t id = TagTypes->choice(); diff --git a/src/utility/type_conversions.cpp b/src/utility/type_conversions.cpp index 5787f4e3..74669277 100644 --- a/src/utility/type_conversions.cpp +++ b/src/utility/type_conversions.cpp @@ -155,3 +155,21 @@ MPD::Song::GetFunction charToGetFunction(char c) return 0; } } + +std::string itemTypeToString(MPD::ItemType type) +{ + std::string result; + switch (type) + { + case MPD::itDirectory: + result = "directory"; + break; + case MPD::itSong: + result = "song"; + break; + case MPD::itPlaylist: + result = "playlist"; + break; + } + return result; +} diff --git a/src/utility/type_conversions.h b/src/utility/type_conversions.h index 631ca107..083151c0 100644 --- a/src/utility/type_conversions.h +++ b/src/utility/type_conversions.h @@ -21,6 +21,7 @@ #ifndef _UTILITY_TYPE_CONVERSIONS #define _UTILITY_TYPE_CONVERSIONS +#include "mpdpp.h" #include "mutable_song.h" std::string tagTypeToString(mpd_tag_type tag); @@ -29,4 +30,6 @@ MPD::MutableSong::SetFunction tagTypeToSetFunction(mpd_tag_type tag); mpd_tag_type charToTagType(char c); MPD::Song::GetFunction charToGetFunction(char c); +std::string itemTypeToString(MPD::ItemType type); + #endif // _UTILITY_TYPE_CONVERSIONS