make playlist and playlist editor share code responsible for moving items

This commit is contained in:
Andrzej Rybczak
2012-09-04 18:14:37 +02:00
parent 60ca9de919
commit bb121fbe01
13 changed files with 424 additions and 378 deletions

View File

@@ -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<size_t> list;
myPlaylist->Items->getSelected(list);
Mpd.StartCommandsList();
for (std::vector<size_t>::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<size_t> 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<size_t> 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<size_t>::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<size_t> 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<size_t>::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<size_t>::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

View File

@@ -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();
};

View File

@@ -50,6 +50,189 @@ inline MPD::Song *currentSong(BasicScreen *screen)
return ptr;
}
template <typename Iterator> bool hasSelected(Iterator first, Iterator last)
{
for (; first != last; ++first)
if (first->isSelected())
return true;
return false;
}
template <typename Iterator> std::vector<Iterator> getSelected(Iterator first, Iterator last)
{
std::vector<Iterator> result;
for (; first != last; ++first)
if (first->isSelected())
result.push_back(first);
return result;
}
template <typename T> void selectCurrentIfNoneSelected(NC::Menu<T> &m)
{
if (!hasSelected(m.begin(), m.end()))
m.current().setSelected(true);
}
template <typename Iterator>
std::vector<Iterator> getSelectedOrCurrent(Iterator first, Iterator last, Iterator current)
{
std::vector<Iterator> result = getSelected(first, last);
if (result.empty())
result.push_back(current);
return result;
}
template <typename T, typename F> void withUnfilteredMenu(NC::Menu<T> &m, F action)
{
bool is_filtered = m.isFiltered();
m.showAll();
action();
if (is_filtered)
m.showFiltered();
}
template <typename T, typename F>
void withUnfilteredMenuReapplyFilter(NC::Menu<T> &m, F action)
{
m.showAll();
action();
if (m.getFilter())
{
m.applyCurrentFilter(m.begin(), m.end());
if (m.empty())
{
m.clearFilter();
m.clearFilterResults();
}
}
}
template <typename T, typename F>
void moveSelectedItemsUp(NC::Menu<T> &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 <typename T, typename F>
void moveSelectedItemsDown(NC::Menu<T> &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 <typename T, typename F>
void moveSelectedItemsTo(NC::Menu<T> &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 <typename Iterator> void reverseSelectionHelper(Iterator first, Iterator last)
{
for (; first != last; ++first)
@@ -69,15 +252,6 @@ template <typename Iterator> std::string getSharedDirectory(Iterator first, Iter
return result;
}
template <typename T> void withUnfilteredMenu(NC::Menu<T> &menu, std::function<void()> action)
{
bool is_filtered = menu.isFiltered();
menu.showAll();
action();
if (is_filtered)
menu.showFiltered();
}
void ParseArgv(int, char **);
template <typename T> struct StringConverter {

View File

@@ -60,6 +60,10 @@ template <typename T> 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 <typename T> 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<size_t> &v) const;
/// Highlights given position
/// @param pos position to be highlighted
void highlight(size_t pos);
@@ -247,11 +247,15 @@ template <typename T> 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 <typename T> struct Menu : public Window
/// @return const reference to item at given position
Menu<T>::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<Item *> *m_options_ptr;
std::vector<Item *> m_options;
std::vector<Item *> m_filtered_options;
std::vector<size_t> m_filtered_positions;
std::set<size_t> m_found_positions;
size_t m_beginning;
@@ -697,13 +705,6 @@ template <typename T> bool Menu<T>::hasSelected() const
return false;
}
template <typename T> void Menu<T>::getSelected(std::vector<size_t> &v) const
{
for (size_t i = 0; i < m_options_ptr->size(); ++i)
if ((*m_options_ptr)[i]->isSelected())
v.push_back(i);
}
template <typename T> void Menu<T>::highlight(size_t pos)
{
m_highlight = pos;
@@ -727,24 +728,33 @@ void Menu<T>::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 <typename T>
void Menu<T>::applyCurrentFilter(ConstIterator first, ConstIterator last)
{
assert(m_filter);
filter(first, last, m_filter);
}
template <typename T> void Menu<T>::clearFilterResults()
{
m_filtered_options.clear();
m_filtered_positions.clear();
m_options_ptr = &m_options;
}
template <typename T> void Menu<T>::clearFilter()
{
m_filter = 0;
}
template <typename T>
bool Menu<T>::search(ConstIterator first, ConstIterator last, const FilterFunction &f)
{

View File

@@ -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;

View File

@@ -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);

View File

@@ -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<size_t> 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<void(MPD::SongList::iterator, MPD::SongList::iterator)> 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<size_t> list;
myPlaylist->Items->getSelected(list);
if (list.front() > 0)
{
Mpd.StartCommandsList();
std::vector<size_t>::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<size_t> list;
Items->getSelected(list);
if (list.back() < Items->size()-1)
{
Mpd.StartCommandsList();
std::vector<size_t>::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<size_t> 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<size_t>::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");
}

View File

@@ -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<std::string>::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<std::string>::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<size_t> list;
Content->getSelected(list);
if (list.front() > 0)
{
Mpd.StartCommandsList();
std::vector<size_t>::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<size_t> list;
Content->getSelected(list);
if (list.back() < Content->size()-1)
{
Mpd.StartCommandsList();
std::vector<size_t>::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;
}
}

View File

@@ -63,6 +63,9 @@ class PlaylistEditor : public Screen<NC::Window>, 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<NC::Window>, public Filterable, public HasS
private:
void AddToPlaylist(bool);
bool playlistsUpdateRequested;
bool contentUpdateRequested;
};
extern PlaylistEditor *myPlaylistEditor;

View File

@@ -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())

View File

@@ -526,14 +526,15 @@ void TagEditor::EnterPressed()
EditedSongs.clear();
if (Tags->hasSelected()) // if there are selected songs, perform operations only on them
{
std::vector<size_t> 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();

View File

@@ -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;
}

View File

@@ -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