You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
351 lines
9.4 KiB
351 lines
9.4 KiB
/*************************************************************************** |
|
* Copyright (C) 2008-2009 by Andrzej Rybczak * |
|
* electricityispower@gmail.com * |
|
* * |
|
* This program is free software; you can redistribute it and/or modify * |
|
* it under the terms of the GNU General Public License as published by * |
|
* the Free Software Foundation; either version 2 of the License, or * |
|
* (at your option) any later version. * |
|
* * |
|
* This program is distributed in the hope that it will be useful, * |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of * |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
|
* GNU General Public License for more details. * |
|
* * |
|
* You should have received a copy of the GNU General Public License * |
|
* along with this program; if not, write to the * |
|
* Free Software Foundation, Inc., * |
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * |
|
***************************************************************************/ |
|
|
|
#include "display.h" |
|
#include "global.h" |
|
#include "helpers.h" |
|
#include "menu.h" |
|
#include "playlist.h" |
|
#include "song.h" |
|
#include "status.h" |
|
#include "tag_editor.h" |
|
|
|
using namespace Global; |
|
using std::vector; |
|
|
|
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() |
|
{ |
|
w = new Menu<MPD::Song>(0, main_start_y, COLS, main_height, Config.columns_in_playlist ? Display::Columns(Config.song_columns_list_format) : "", Config.main_color, brNone); |
|
w->SetTimeout(ncmpcpp_window_timeout); |
|
w->HighlightColor(Config.main_highlight_color); |
|
w->SetSelectPrefix(&Config.selected_item_prefix); |
|
w->SetSelectSuffix(&Config.selected_item_suffix); |
|
w->SetItemDisplayer(Config.columns_in_playlist ? Display::SongsInColumns : Display::Songs); |
|
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() |
|
{ |
|
if (myScreen == this) |
|
return; |
|
|
|
if (hasToBeResized) |
|
Resize(); |
|
|
|
CLEAR_FIND_HISTORY; |
|
myScreen = this; |
|
w->Window::Clear(); |
|
redraw_header = 1; |
|
} |
|
|
|
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; |
|
} |
|
|
|
std::string Playlist::Title() |
|
{ |
|
std::string result = "Playlist "; |
|
result += TotalLength(); |
|
return result; |
|
} |
|
|
|
void Playlist::EnterPressed() |
|
{ |
|
if (!w->Empty()) |
|
Mpd->PlayID(w->Current().GetID()); |
|
} |
|
|
|
void Playlist::SpacePressed() |
|
{ |
|
w->SelectCurrent(); |
|
w->Scroll(wDown); |
|
} |
|
|
|
MPD::Song *Playlist::CurrentSong() |
|
{ |
|
return !w->Empty() ? &w->Current() : 0; |
|
} |
|
|
|
void Playlist::GetSelectedSongs(MPD::SongList &v) |
|
{ |
|
vector<size_t> selected; |
|
w->GetSelected(selected); |
|
for (vector<size_t>::const_iterator it = selected.begin(); it != selected.end(); it++) |
|
{ |
|
v.push_back(new MPD::Song(w->at(*it))); |
|
} |
|
} |
|
|
|
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; |
|
|
|
const int MINUTE = 60; |
|
const int HOUR = 60*MINUTE; |
|
const int DAY = 24*HOUR; |
|
const int YEAR = 365*DAY; |
|
int length = 0; |
|
|
|
for (size_t i = 0; i < w->Size(); i++) |
|
length += w->at(i).GetTotalLength(); |
|
|
|
result << '(' << w->Size() << (w->Size() == 1 ? " item" : " items"); |
|
|
|
if (w->isFiltered()) |
|
{ |
|
w->ShowAll(); |
|
size_t real_size = w->Size(); |
|
w->ShowFiltered(); |
|
if (w->Size() != real_size) |
|
result << " (out of " << Mpd->GetPlaylistLength() << ")"; |
|
} |
|
|
|
if (length) |
|
{ |
|
result << ", length: "; |
|
int years = length/YEAR; |
|
if (years) |
|
{ |
|
result << years << (years == 1 ? " year" : " years"); |
|
length -= years*YEAR; |
|
if (length) |
|
result << ", "; |
|
} |
|
int days = length/DAY; |
|
if (days) |
|
{ |
|
result << days << (days == 1 ? " day" : " days"); |
|
length -= days*DAY; |
|
if (length) |
|
result << ", "; |
|
} |
|
int hours = length/HOUR; |
|
if (hours) |
|
{ |
|
result << hours << (hours == 1 ? " hour" : " hours"); |
|
length -= hours*HOUR; |
|
if (length) |
|
result << ", "; |
|
} |
|
int minutes = length/MINUTE; |
|
if (minutes) |
|
{ |
|
result << minutes << (minutes == 1 ? " minute" : " minutes"); |
|
length -= minutes*MINUTE; |
|
if (length) |
|
result << ", "; |
|
} |
|
if (length) |
|
result << length << (length == 1 ? " second" : " seconds"); |
|
} |
|
result << ')'; |
|
return result.str(); |
|
} |
|
|
|
const MPD::Song &Playlist::NowPlayingSong() |
|
{ |
|
static MPD::Song null; |
|
return isPlaying() ? w->at(NowPlaying) : null; |
|
} |
|
|
|
std::string Playlist::SongToString(const MPD::Song &s, void *data) |
|
{ |
|
return s.toString(*static_cast<std::string *>(data)); |
|
} |
|
|
|
std::string Playlist::SongInColumnsToString(const MPD::Song &s, void *data) |
|
{ |
|
std::string result; |
|
std::string fmt = *static_cast<std::string *>(data); |
|
for (std::string i = GetLineValue(fmt, '{', '}', 1); !i.empty(); i = GetLineValue(fmt, '{', '}', 1)) |
|
{ |
|
result += "%"; |
|
result += i; |
|
result += " "; |
|
} |
|
return s.toString(result); |
|
} |
|
|
|
|