new feature: playlist sorting (Ctrl-V by default)

This commit is contained in:
Andrzej Rybczak
2009-02-18 20:08:50 +01:00
parent 70f48dd454
commit f162a7b880
17 changed files with 215 additions and 30 deletions

View File

@@ -82,6 +82,8 @@
#
#key_update_db = 'u'
#
#key_sort_playlist = 22
#
#key_apply_filter = 6
#
#key_find_forward = '/'

View File

@@ -90,11 +90,6 @@ string Display::Columns(string st)
return result;
}
void Display::StringPairs(const string_pair &pair, void *, Menu<string_pair> *menu)
{
*menu << pair.first;
}
void Display::SongsInColumns(const MPD::Song &s, void *s_template, Menu<MPD::Song> *menu)
{
string st = s_template ? *static_cast<string *>(s_template) : "";
@@ -115,7 +110,7 @@ void Display::SongsInColumns(const MPD::Song &s, void *s_template, Menu<MPD::Son
color = IntoColor(GetLineValue(st, '[', ']', 1));
char type = GetLineValue(st, '{', '}', 1)[0];
string (Song::*get)() const = 0;
Song::GetFunction get = 0;
switch (type)
{
@@ -195,7 +190,7 @@ void Display::Songs(const MPD::Song &s, void *data, Menu<MPD::Song> *menu)
if (*it == '{')
{
prev_pos = it;
string (Song::*get)() const = 0;
Song::GetFunction get = 0;
for (; *it != '}'; it++)
{
if (*it == '%')

View File

@@ -34,7 +34,10 @@ namespace Display
*menu << t;
}
void StringPairs(const string_pair &, void *, Menu<string_pair> *);
template <class A, class B> void Pairs(const std::pair<A, B> &pair, void *, Menu< std::pair<A, B> > *menu)
{
*menu << pair.first;
}
void SongsInColumns(const MPD::Song &, void *, Menu<MPD::Song> *);
@@ -45,8 +48,6 @@ namespace Display
void SearchEngine(const std::pair<Buffer *, MPD::Song *> &, void *, Menu< std::pair<Buffer *, MPD::Song *> > *);
void Items(const MPD::Item &, void *, Menu<MPD::Item> *);
void Clock(Window &, const tm *);
}
#endif

View File

@@ -199,6 +199,7 @@ void Help::GetKeybindings()
*w << DisplayKeys(Key.MvSongDown) << "Move item/group of items down\n";
*w << DisplayKeys(Key.Add) << "Add url/file/directory to playlist\n";
*w << DisplayKeys(Key.SavePlaylist) << "Save playlist\n";
*w << DisplayKeys(Key.SortPlaylist) << "Sort playlist\n";
*w << DisplayKeys(Key.GoToNowPlaying) << "Go to currently playing position\n";
*w << DisplayKeys(Key.ToggleAutoCenter) << "Toggle auto center mode\n\n\n";

View File

@@ -57,7 +57,7 @@ void MediaLibrary::Init()
Albums = new Menu<string_pair>(itsMiddleColStartX, main_start_y, itsMiddleColWidth, main_height, "Albums", Config.main_color, brNone);
Albums->HighlightColor(Config.main_highlight_color);
Albums->SetTimeout(ncmpcpp_window_timeout);
Albums->SetItemDisplayer(Display::StringPairs);
Albums->SetItemDisplayer(Display::Pairs);
Albums->SetGetStringFunction(StringPairToString);
Songs = new Menu<Song>(itsRightColStartX, main_start_y, itsRightColWidth, main_height, "Songs", Config.main_color, brNone);

View File

@@ -290,6 +290,15 @@ void Connection::Move(int from, int to) const
}
}
void Connection::Swap(int from, int to) const
{
if (isConnected)
{
mpd_sendSwapCommand(itsConnection, from, to);
mpd_finishCommand(itsConnection);
}
}
void Connection::Seek(int where) const
{
if (isConnected)

View File

@@ -119,6 +119,7 @@ namespace MPD
void Next() const;
void Prev() const;
void Move(int, int) const;
void Swap(int, int) const;
void Seek(int) const;
void Shuffle() const;
void ClearPlaylist() const;

View File

@@ -1042,7 +1042,7 @@ int main(int argc, char *argv[])
Mpd->StartSearch(1);
Mpd->AddSearch(Config.media_lib_primary_tag, locale_to_utf_cpy(myLibrary->Artists->Current()));
Mpd->CommitSearch(list);
SongSetFunction set = IntoSetFunction(Config.media_lib_primary_tag);
Song::SetFunction set = IntoSetFunction(Config.media_lib_primary_tag);
if (!set)
continue;
for (SongList::iterator it = list.begin(); it != list.end(); it++)
@@ -1290,9 +1290,7 @@ int main(int argc, char *argv[])
mDialog->Display();
myOldScreen = myScreen;
myScreen = myHelp; // temp hack, prevent playlist from updating
Playlist::BlockRefreshing = 1;
while (!Keypressed(input, Key.Enter))
{
TraceMpdStatus();
@@ -1312,8 +1310,7 @@ int main(int argc, char *argv[])
else if (Keypressed(input, Key.End))
mDialog->Scroll(wEnd);
}
myScreen = myOldScreen;
Playlist::BlockRefreshing = 0;
size_t id = mDialog->Choice();
@@ -1412,6 +1409,12 @@ int main(int argc, char *argv[])
Mpd->ClearPlaylist();
ShowMessage("Cleared playlist!");
}
else if (Keypressed(input, Key.SortPlaylist) && myScreen == myPlaylist)
{
myPlaylist->Sort();
myPlaylist->Main()->Highlighting(1);
time(&timer);
}
else if (Keypressed(input, Key.ApplyFilter))
{
List *mList = myScreen->GetList();

View File

@@ -34,6 +34,14 @@ Playlist *myPlaylist = new Playlist;
bool Playlist::BlockNowPlayingUpdate = 0;
bool Playlist::BlockUpdate = 0;
bool Playlist::BlockRefreshing = 0;
Menu< std::pair<std::string, MPD::Song::GetFunction> > *Playlist::SortDialog;
const size_t Playlist::SortOptions = 10;
const size_t Playlist::SortDialogWidth = 30;
const size_t Playlist::SortDialogHeight = 17;
void Playlist::Init()
{
@@ -46,6 +54,24 @@ void Playlist::Init()
w->SetItemDisplayerUserData(Config.columns_in_playlist ? &Config.song_columns_list_format : &Config.song_list_format);
w->SetGetStringFunction(Config.columns_in_playlist ? SongInColumnsToString : SongToString);
w->SetGetStringFunctionUserData(Config.columns_in_playlist ? &Config.song_columns_list_format : &Config.song_list_format);
SortDialog = new Menu< std::pair<std::string, MPD::Song::GetFunction> >((COLS-SortDialogWidth)/2, (LINES-SortDialogHeight)/2, SortDialogWidth, SortDialogHeight, "Sort songs by...", Config.main_color, Config.window_border);
SortDialog->SetTimeout(ncmpcpp_window_timeout);
SortDialog->SetItemDisplayer(Display::Pairs);
SortDialog->AddOption(std::make_pair("Artist", &MPD::Song::GetArtist));
SortDialog->AddOption(std::make_pair("Album", &MPD::Song::GetAlbum));
SortDialog->AddOption(std::make_pair("Disc", &MPD::Song::GetDisc));
SortDialog->AddOption(std::make_pair("Track", &MPD::Song::GetTrack));
SortDialog->AddOption(std::make_pair("Genre", &MPD::Song::GetGenre));
SortDialog->AddOption(std::make_pair("Year", &MPD::Song::GetYear));
SortDialog->AddOption(std::make_pair("Composer", &MPD::Song::GetComposer));
SortDialog->AddOption(std::make_pair("Performer", &MPD::Song::GetPerformer));
SortDialog->AddOption(std::make_pair("Title", &MPD::Song::GetTitle));
SortDialog->AddOption(std::make_pair("Filename", &MPD::Song::GetFile));
SortDialog->AddSeparator();
SortDialog->AddOption(std::make_pair("Sort", (MPD::Song::GetFunction)0));
SortDialog->AddOption(std::make_pair("Cancel", (MPD::Song::GetFunction)0));
}
void Playlist::SwitchTo()
@@ -66,6 +92,7 @@ void Playlist::Resize()
{
w->Resize(COLS, main_height);
w->SetTitle(Config.columns_in_playlist ? Display::Columns(Config.song_columns_list_format) : "");
SortDialog->MoveTo((COLS-SortDialogWidth)/2, (LINES-SortDialogHeight)/2);
hasToBeResized = 0;
}
@@ -103,6 +130,135 @@ void Playlist::GetSelectedSongs(MPD::SongList &v)
}
}
void Playlist::Sort()
{
if (w->GetWidth() < SortDialogWidth || w->GetHeight() < SortDialogHeight)
{
ShowMessage("Screen is too small to display dialog window!");
return;
}
int input;
SortDialog->Reset();
SortDialog->Display();
BlockRefreshing = 1;
while (1)
{
TraceMpdStatus();
SortDialog->Refresh();
SortDialog->ReadKey(input);
if (Keypressed(input, Key.Up))
{
SortDialog->Scroll(wUp);
}
else if (Keypressed(input, Key.Down))
{
SortDialog->Scroll(wDown);
}
else if (Keypressed(input, Key.PageUp))
{
SortDialog->Scroll(wPageUp);
}
else if (Keypressed(input, Key.PageDown))
{
SortDialog->Scroll(wPageDown);
}
else if (Keypressed(input, Key.Home))
{
SortDialog->Scroll(wHome);
}
else if (Keypressed(input, Key.End))
{
SortDialog->Scroll(wEnd);
}
else if (Keypressed(input, Key.MvSongUp))
{
size_t pos = SortDialog->Choice();
if (pos > 0 && pos < SortOptions)
{
SortDialog->Swap(pos, pos-1);
SortDialog->Scroll(wUp);
}
}
else if (Keypressed(input, Key.MvSongDown))
{
size_t pos = SortDialog->Choice();
if (pos < SortOptions-1)
{
SortDialog->Swap(pos, pos+1);
SortDialog->Scroll(wDown);
}
}
else if (Keypressed(input, Key.Enter))
{
size_t pos = SortDialog->Choice();
if (pos > SortOptions)
{
BlockRefreshing = 0;
if (pos == SortOptions+1) // sort
break;
else if (pos == SortOptions+2) // cancel
return;
}
else
{
ShowMessage("Move tag types up and down to adjust sort order");
}
}
}
MPD::SongList playlist, cmp;
playlist.reserve(w->Size());
for (size_t i = 0; i < w->Size(); i++)
{
(*w)[i].SetPosition(i);
playlist.push_back(&(*w)[i]);
}
cmp = playlist;
sort(playlist.begin(), playlist.end(), Playlist::Sorting);
if (playlist == cmp)
{
ShowMessage("Playlist is already sorted");
return;
}
ShowMessage("Sorting playlist...");
do
{
for (size_t i = 0; i < playlist.size(); i++)
{
if (playlist[i]->GetPosition() > int(i))
{
Mpd->Swap(playlist[i]->GetPosition(), i);
std::swap(cmp[playlist[i]->GetPosition()], cmp[i]);
}
cmp[i]->SetPosition(i);
}
}
while (playlist != cmp);
ShowMessage("Playlist sorted!");
}
bool Playlist::Sorting(MPD::Song *a, MPD::Song *b)
{
for (size_t i = 0; i < SortOptions; i++)
{
MPD::Song::GetFunction get = (*SortDialog)[i].second;
if ((a->*get)() != (b->*get)())
{
return (a->*get)() < (b->*get)();
}
}
return a->GetPosition() < b->GetPosition();
}
std::string Playlist::TotalLength()
{
std::ostringstream result;

View File

@@ -53,6 +53,8 @@ class Playlist : public Screen< Menu<MPD::Song> >
bool isPlaying() { return NowPlaying >= 0 && !w->Empty(); }
const MPD::Song &NowPlayingSong();
void Sort();
static std::string SongToString(const MPD::Song &, void *);
static std::string SongInColumnsToString(const MPD::Song &, void *);
@@ -61,9 +63,18 @@ class Playlist : public Screen< Menu<MPD::Song> >
static bool BlockNowPlayingUpdate;
static bool BlockUpdate;
static bool BlockRefreshing;
protected:
std::string TotalLength();
static bool Sorting(MPD::Song *a, MPD::Song *b);
static Menu< std::pair<std::string, MPD::Song::GetFunction> > *SortDialog;
static const size_t SortOptions;
static const size_t SortDialogWidth;
static const size_t SortDialogHeight;
};
extern Playlist *myPlaylist;

View File

@@ -117,6 +117,7 @@ void DefaultKeys(ncmpcpp_keys &keys)
keys.ToggleCrossfade[0] = 'x';
keys.SetCrossfade[0] = 'X';
keys.UpdateDB[0] = 'u';
keys.SortPlaylist[0] = 22;
keys.ApplyFilter[0] = 6;
keys.FindForward[0] = '/';
keys.FindBackward[0] = '?';
@@ -182,6 +183,7 @@ void DefaultKeys(ncmpcpp_keys &keys)
keys.ToggleCrossfade[1] = null_key;
keys.SetCrossfade[1] = null_key;
keys.UpdateDB[1] = null_key;
keys.SortPlaylist[1] = null_key;
keys.ApplyFilter[1] = null_key;
keys.FindForward[1] = null_key;
keys.FindBackward[1] = null_key;
@@ -356,6 +358,8 @@ void ReadKeys(ncmpcpp_keys &keys)
GetKeys(key, keys.SetCrossfade);
else if (key.find("key_update_db ") != string::npos)
GetKeys(key, keys.UpdateDB);
else if (key.find("key_sort_playlist ") != string::npos)
GetKeys(key, keys.SortPlaylist);
else if (key.find("key_apply_filter ") != string::npos)
GetKeys(key, keys.ApplyFilter);
else if (key.find("key_find_forward ") != string::npos)

View File

@@ -66,6 +66,7 @@ struct ncmpcpp_keys
int ToggleCrossfade[2];
int SetCrossfade[2];
int UpdateDB[2];
int SortPlaylist[2];
int ApplyFilter[2];
int FindForward[2];
int FindBackward[2];

View File

@@ -299,7 +299,7 @@ string Song::toString(const std::string &format) const
if (*it == '{')
{
prev_pos = it;
string (Song::*get)() const = 0;
GetFunction get = 0;
for (; *it != '}'; it++)
{
if (*it == '%')

View File

@@ -31,6 +31,10 @@ namespace MPD
class Song
{
public:
typedef void (Song::*SetFunction)(const std::string &);
typedef std::string (Song::*GetFunction)() const;
Song() : itsSlash(std::string::npos), itsHash(0), copyPtr(0), isStream(0), isLocalised(0) { itsSong = mpd_newSong(); }
Song(mpd_Song *, bool = 0);
Song(const Song &);

View File

@@ -470,7 +470,7 @@ void NcmpcppStatusChanged(Connection *Mpd, StatusChanges changed, void *)
wHeader->SetColor(Config.header_color);
wHeader->Refresh();
}
if (myScreen == myPlaylist)
if (myScreen == myPlaylist && !Playlist::BlockRefreshing)
myPlaylist->Main()->Refresh();
wFooter->Bold(0);
wFooter->GotoXY(sx, sy);

View File

@@ -303,13 +303,13 @@ void TagEditor::Init()
Albums = new Menu<string_pair>(0, main_start_y, LeftColumnWidth, main_height, "Albums", Config.main_color, brNone);
Albums->HighlightColor(Config.active_column_color);
Albums->SetTimeout(ncmpcpp_window_timeout);
Albums->SetItemDisplayer(Display::StringPairs);
Albums->SetItemDisplayer(Display::Pairs);
Albums->SetGetStringFunction(MediaLibrary::StringPairToString);
Dirs = new Menu<string_pair>(0, main_start_y, LeftColumnWidth, main_height, "Directories", Config.main_color, brNone);
Dirs->HighlightColor(Config.active_column_color);
Dirs->SetTimeout(ncmpcpp_window_timeout);
Dirs->SetItemDisplayer(Display::StringPairs);
Dirs->SetItemDisplayer(Display::Pairs);
Dirs->SetGetStringFunction(MediaLibrary::StringPairToString);
LeftColumn = Config.albums_in_tag_editor ? Albums : Dirs;
@@ -537,8 +537,8 @@ void TagEditor::EnterPressed()
for (size_t i = 0; i < Tags->Size(); i++)
list.push_back(&Tags->at(i));
SongGetFunction get = 0;
SongSetFunction set = 0;
Song::GetFunction get = 0;
Song::SetFunction set = 0;
size_t id = TagTypes->RealChoice();
switch (id)
@@ -1057,7 +1057,7 @@ namespace
}
}
SongSetFunction IntoSetFunction(char c)
Song::SetFunction IntoSetFunction(char c)
{
switch (c)
{
@@ -1133,7 +1133,7 @@ namespace
if (!preview)
{
SongSetFunction set = IntoSetFunction(it->first);
Song::SetFunction set = IntoSetFunction(it->first);
if (set)
(s.*set)(it->second);
}
@@ -1144,7 +1144,7 @@ namespace
}
}
SongSetFunction IntoSetFunction(mpd_TagItems tag)
Song::SetFunction IntoSetFunction(mpd_TagItems tag)
{
switch (tag)
{

View File

@@ -117,13 +117,10 @@ class TagEditor : public Screen<Window>
extern TagEditor *myTagEditor;
typedef void (MPD::Song::*SongSetFunction)(const std::string &);
typedef std::string (MPD::Song::*SongGetFunction)() const;
std::string FindSharedDir(Menu<MPD::Song> *);
std::string FindSharedDir(const MPD::SongList &);
SongSetFunction IntoSetFunction(mpd_TagItems);
MPD::Song::SetFunction IntoSetFunction(mpd_TagItems);
void DealWithFilenames(MPD::SongList &);