implement HasSongs interface

This commit is contained in:
Andrzej Rybczak
2012-09-02 13:47:57 +02:00
parent ad8fef343f
commit 6f59a175ec
37 changed files with 840 additions and 705 deletions

View File

@@ -19,7 +19,6 @@ ncmpcpp_SOURCES = \
lyrics.cpp \
lyrics_fetcher.cpp \
media_library.cpp \
menu.cpp \
mpdpp.cpp \
mutable_song.cpp \
ncmpcpp.cpp \

View File

@@ -503,7 +503,8 @@ void ScrollDown::Run()
void ScrollUpArtist::Run()
{
List *mList = myScreen->GetList();
// FIXME
/*List *mList = myScreen->GetList();
if (!mList || mList->Empty())
return;
size_t pos = mList->Choice();
@@ -517,12 +518,13 @@ void ScrollUpArtist::Run()
if (!s || s->getArtist() != artist)
break;
}
}
}*/
}
void ScrollUpAlbum::Run()
{
List *mList = myScreen->GetList();
// FIXME
/*List *mList = myScreen->GetList();
if (!mList || mList->Empty())
return;
size_t pos = mList->Choice();
@@ -536,12 +538,13 @@ void ScrollUpAlbum::Run()
if (!s || s->getAlbum() != album)
break;
}
}
}*/
}
void ScrollDownArtist::Run()
{
List *mList = myScreen->GetList();
// FIXME
/*List *mList = myScreen->GetList();
if (!mList || mList->Empty())
return;
size_t pos = mList->Choice();
@@ -555,12 +558,13 @@ void ScrollDownArtist::Run()
if (!s || s->getArtist() != artist)
break;
}
}
}*/
}
void ScrollDownAlbum::Run()
{
List *mList = myScreen->GetList();
// FIXME
/*List *mList = myScreen->GetList();
if (!mList || mList->Empty())
return;
size_t pos = mList->Choice();
@@ -574,7 +578,7 @@ void ScrollDownAlbum::Run()
if (!s || s->getAlbum() != album)
break;
}
}
}*/
}
void PageUp::Run()
@@ -769,7 +773,7 @@ void Delete::Run()
}
}
else
Mpd.DeleteID(myPlaylist->CurrentSong()->getID());
Mpd.DeleteID(myPlaylist->currentSong()->getID());
}
else if (
(myScreen == myBrowser && !myBrowser->Main()->Empty() && myBrowser->CurrentDir() == "/" && myBrowser->Main()->Current().value().type == itPlaylist)
@@ -1184,13 +1188,9 @@ void ToggleDisplayMode::Run()
Config.columns_in_playlist_editor = !Config.columns_in_playlist_editor;
ShowMessage("Playlist editor display mode: %s", Config.columns_in_playlist_editor ? "Columns" : "Classic");
if (Config.columns_in_playlist_editor)
{
myPlaylistEditor->Content->setItemDisplayer(std::bind(Display::SongsInColumns, _1, *myPlaylistEditor));
}
else
{
myPlaylistEditor->Content->setItemDisplayer(std::bind(Display::Songs, _1, *myPlaylistEditor, Config.song_list_format));
}
}
}
@@ -1377,7 +1377,8 @@ void SetCrossfade::Run()
bool EditSong::canBeRun() const
{
# ifdef HAVE_TAGLIB_H
return myScreen->CurrentSong();
auto w = dynamic_cast<HasSongs *>(myScreen);
return w && w->currentSong();
# else
return false;
# endif // HAVE_TAGLIB_H
@@ -1388,7 +1389,9 @@ void EditSong::Run()
# ifdef HAVE_TAGLIB_H
if (!isMPDMusicDirSet())
return;
const MPD::Song *s = myScreen->CurrentSong();
auto w = dynamic_cast<HasSongs *>(myScreen);
assert(w);
auto s = w->currentSong();
assert(s);
myTinyTagEditor->SetEdited(*s);
myTinyTagEditor->SwitchTo();
@@ -1424,11 +1427,10 @@ void EditLibraryTag::Run()
MPD::MutableSong::SetFunction set = tagTypeToSetFunction(Config.media_lib_primary_tag);
assert(set);
bool success = true;
std::string dir_to_update;
Mpd.CommitSearchSongs([set, &new_tag, &success, &dir_to_update](MPD::Song &&s) {
if (!success)
return;
MPD::MutableSong es = s;
MPD::SongList songs = Mpd.CommitSearchSongs();
for (auto s = songs.begin(); s != songs.end(); ++s)
{
MPD::MutableSong es = *s;
es.setTag(set, new_tag);
ShowMessage("Updating tags in \"%s\"...", es.getName().c_str());
std::string path = Config.mpd_music_dir + es.getURI();
@@ -1437,15 +1439,12 @@ void EditLibraryTag::Run()
const char msg[] = "Error while updating tags in \"%s\"";
ShowMessage(msg, Shorten(TO_WSTRING(es.getURI()), COLS-const_strlen(msg)).c_str());
success = false;
break;
}
if (dir_to_update.empty())
dir_to_update = es.getDirectory();
else
getSharedDirectory(es.getDirectory(), dir_to_update);
});
}
if (success)
{
Mpd.UpdateDirectory(dir_to_update);
Mpd.UpdateDirectory(getSharedDirectory(songs.begin(), songs.end()));
ShowMessage("Tags updated successfully");
}
}
@@ -1500,7 +1499,7 @@ void EditLibraryAlbum::Run()
}
if (success)
{
Mpd.UpdateDirectory(getSharedDirectory(myLibrary->Songs));
Mpd.UpdateDirectory(getSharedDirectory(myLibrary->Songs->BeginV(), myLibrary->Songs->EndV()));
ShowMessage("Tags updated successfully");
}
}
@@ -1635,24 +1634,29 @@ void EditLyrics::Run()
bool JumpToBrowser::canBeRun() const
{
return myScreen->CurrentSong();
auto w = dynamic_cast<HasSongs *>(myScreen);
return w && w->currentSong();
}
void JumpToBrowser::Run()
{
MPD::Song *s = myScreen->CurrentSong();
auto w = dynamic_cast<HasSongs *>(myScreen);
auto s = w->currentSong();
assert(s);
myBrowser->LocateSong(*s);
}
bool JumpToMediaLibrary::canBeRun() const
{
return myScreen->CurrentSong();
auto w = dynamic_cast<HasSongs *>(myScreen);
return w && w->currentSong();
}
void JumpToMediaLibrary::Run()
{
MPD::Song *s = myScreen->CurrentSong();
auto w = dynamic_cast<HasSongs *>(myScreen);
assert(w);
auto s = w->currentSong();
assert(s);
myLibrary->LocateSong(*s);
}
@@ -1708,7 +1712,8 @@ void ToggleScreenLock::Run()
bool JumpToTagEditor::canBeRun() const
{
# ifdef HAVE_TAGLIB_H
return myScreen->CurrentSong();
auto w = dynamic_cast<HasSongs *>(myScreen);
return w && w->currentSong();
# else
return false;
# endif // HAVE_TAGLIB_H
@@ -1719,7 +1724,9 @@ void JumpToTagEditor::Run()
# ifdef HAVE_TAGLIB_H
if (!isMPDMusicDirSet())
return;
MPD::Song *s = myScreen->CurrentSong();
auto w = dynamic_cast<HasSongs *>(myScreen);
assert(w);
auto s = w->currentSong();
assert(s);
myTagEditor->LocateSong(*s);
# endif // HAVE_TAGLIB_H
@@ -1780,32 +1787,35 @@ void JumpToPositionInSong::Run()
bool ReverseSelection::canBeRun() const
{
return myScreen->allowsSelection();
auto w = dynamic_cast<HasSongs *>(myScreen);
return w && w->allowsSelection();
}
void ReverseSelection::Run()
{
myScreen->ReverseSelection();
auto w = dynamic_cast<HasSongs *>(myScreen);
assert(w);
w->reverseSelection();
ShowMessage("Selection reversed");
}
bool DeselectItems::canBeRun() const
{
return myScreen->allowsSelection();
auto w = dynamic_cast<HasSongs *>(myScreen);
return w && w->allowsSelection();
}
void DeselectItems::Run()
{
// FIXME
/*List *mList = myScreen->GetList();
for (size_t i = 0; i < mList->Size(); ++i)
mList->Select(i, 0);
ShowMessage("Items deselected");*/
auto w = dynamic_cast<HasSongs *>(myScreen);
assert(w);
w->removeSelection();
}
bool SelectAlbum::canBeRun() const
{
return myScreen->allowsSelection()
auto w = dynamic_cast<HasSongs *>(myScreen);
return w && w->allowsSelection()
&& myScreen->GetList();
}
@@ -2334,12 +2344,14 @@ void ShowSongInfo::Run()
mySongInfo->SwitchTo();
}
#ifndef HAVE_CURL_CURL_H
bool ShowArtistInfo::canBeRun() const
{
#ifdef HAVE_CURL_CURL_H
return myScreen == myLastfm || dynamic_cast<HasSongs *>(myScreen);
# else
return false;
# endif // NOT HAVE_CURL_CURL_H
}
#endif // NOT HAVE_CURL_CURL_H
void ShowArtistInfo::Run()
{
@@ -2351,7 +2363,9 @@ void ShowArtistInfo::Run()
}
std::string artist;
MPD::Song *s = myScreen->CurrentSong();
auto hs = dynamic_cast<HasSongs *>(myScreen);
assert(hs);
auto s = hs->currentSong();
if (s)
artist = s->getArtist();

View File

@@ -766,9 +766,7 @@ struct ShowSongInfo : public Action
struct ShowArtistInfo : public Action
{
ShowArtistInfo() : Action(aShowArtistInfo, "show_artist_info") { }
# ifndef HAVE_CURL_CURL_H
virtual bool canBeRun() const;
# endif // NOT HAVE_CURL_CURL_H
virtual void Run();
};

View File

@@ -263,68 +263,6 @@ void Browser::MouseButtonPressed(MEVENT me)
Screen< Menu<MPD::Item> >::MouseButtonPressed(me);
}
MPD::Song *Browser::CurrentSong()
{
const MPD::Item &item = w->Current().value();
if (!w->Empty() && item.type == itSong)
return item.song.get();
else
return 0;
}
void Browser::ReverseSelection()
{
w->ReverseSelection(itsBrowsedDir == "/" ? 0 : 1);
}
void Browser::GetSelectedSongs(MPD::SongList &v)
{
if (w->Empty())
return;
std::vector<size_t> selected;
w->GetSelected(selected);
if (selected.empty())
selected.push_back(w->Choice());
for (auto it = selected.begin(); it != selected.end(); ++it)
{
const MPD::Item &item = w->at(*it).value();
switch (item.type)
{
case itDirectory:
{
# ifndef WIN32
if (isLocal())
{
MPD::ItemList list;
GetLocalDirectory(list, item.name, 1);
for (auto j = list.begin(); j != list.end(); ++j)
v.push_back(*j->song);
}
else
# endif // !WIN32
{
Mpd.GetDirectoryRecursive(locale_to_utf_cpy(item.name), [&v](MPD::Song &&s) {
v.push_back(s);
});
}
break;
}
case itSong:
{
v.push_back(*item.song);
break;
}
case itPlaylist:
{
Mpd.GetPlaylistContent(locale_to_utf_cpy(item.name), [&v](MPD::Song &&s) {
v.push_back(s);
});
break;
}
}
}
}
/***********************************************************************/
std::string Browser::currentFilter()
@@ -361,6 +299,75 @@ void Browser::prevFound(bool wrap)
/***********************************************************************/
MPD::Song *Browser::getSong(size_t pos)
{
MPD::Song *ptr = 0;
if ((*w)[pos].value().type == itSong)
ptr = (*w)[pos].value().song.get();
return ptr;
}
MPD::Song *Browser::currentSong()
{
if (w->Empty())
return 0;
else
return getSong(w->Choice());
}
bool Browser::allowsSelection()
{
return true;
}
void Browser::removeSelection()
{
removeSelectionHelper(w->Begin(), w->End());
}
void Browser::reverseSelection()
{
reverseSelectionHelper(w->Begin()+(itsBrowsedDir == "/" ? 0 : 1), w->End());
}
MPD::SongList Browser::getSelectedSongs()
{
MPD::SongList result;
auto item_handler = [this, &result](const MPD::Item &item) {
if (item.type == itDirectory)
{
# ifndef WIN32
if (isLocal())
{
MPD::ItemList list;
GetLocalDirectory(list, item.name, true);
for (auto it = list.begin(); it != list.end(); ++it)
result.push_back(*it->song);
}
else
# endif // !WIN32
{
auto list = Mpd.GetDirectoryRecursive(item.name);
result.insert(result.end(), list.begin(), list.end());
}
}
else if (item.type == itSong)
result.push_back(*item.song);
else if (item.type == itPlaylist)
{
auto list = Mpd.GetPlaylistContent(item.name);
result.insert(result.end(), list.begin(), list.end());
}
};
for (auto it = w->Begin(); it != w->End(); ++it)
if (it->isSelected())
item_handler(it->value());
// if no item is selected, add current one
if (result.empty() && !w->Empty())
item_handler(w->Current().value());
return result;
}
void Browser::LocateSong(const MPD::Song &s)
{
if (s.getDirectory().empty())
@@ -411,15 +418,9 @@ void Browser::GetDirectory(std::string dir, std::string subdir)
if (isLocal())
GetLocalDirectory(list);
else
{
Mpd.GetDirectory(dir, [&list](MPD::Item &&i) {
list.push_back(i);
});
}
list = Mpd.GetDirectory(dir);
# else
Mpd.GetDirectory(dir, [&list](MPD::Item &&i) {
list.push_back(i);
});
list = Mpd.GetDirectory(dir);
# endif // !WIN32
if (!isLocal()) // local directory is already sorted
std::sort(list.begin(), list.end(), CaseInsensitiveSorting());

View File

@@ -26,7 +26,7 @@
#include "regex_filter.h"
#include "screen.h"
class Browser : public Screen< Menu<MPD::Item> >, public Filterable, public Searchable
class Browser : public Screen< Menu<MPD::Item> >, public Filterable, public HasSongs, public Searchable
{
public:
Browser() : itsBrowseLocally(0), itsScrollBeginning(0), itsBrowsedDir("/") { }
@@ -41,13 +41,6 @@ class Browser : public Screen< Menu<MPD::Item> >, public Filterable, public Sear
virtual void MouseButtonPressed(MEVENT);
virtual bool isTabbable() { return true; }
virtual MPD::Song *CurrentSong();
virtual MPD::Song *GetSong(size_t pos) { return w->at(pos).value().type == MPD::itSong ? (*w)[pos].value().song.get() : 0; }
virtual bool allowsSelection() { return true; }
virtual void ReverseSelection();
virtual void GetSelectedSongs(MPD::SongList &);
/// Filterable implementation
virtual std::string currentFilter();
virtual void applyFilter(const std::string &filter);
@@ -57,6 +50,15 @@ class Browser : public Screen< Menu<MPD::Item> >, public Filterable, public Sear
virtual void nextFound(bool wrap);
virtual void prevFound(bool wrap);
/// HasSongs implementation
virtual MPD::Song *getSong(size_t pos);
virtual MPD::Song *currentSong();
virtual bool allowsSelection();
virtual void reverseSelection();
virtual void removeSelection();
virtual MPD::SongList getSelectedSongs();
virtual List *GetList() { return w; }
virtual bool isMergable() { return true; }

View File

@@ -73,13 +73,13 @@ const my_char_t *toColumnName(char c)
}
template <typename T>
void setProperties(Menu<T> &menu, const MPD::Song &s, BasicScreen &screen, bool &separate_albums,
void setProperties(Menu<T> &menu, const MPD::Song &s, HasSongs &screen, bool &separate_albums,
bool &is_now_playing, bool &is_selected, bool &discard_colors)
{
separate_albums = false;
if (Config.playlist_separate_albums)
{
MPD::Song *next = screen.GetSong(menu.DrawnPosition()+1);
auto next = screen.getSong(menu.DrawnPosition()+1);
if (next && next->getAlbum() != s.getAlbum())
separate_albums = true;
}
@@ -87,13 +87,14 @@ void setProperties(Menu<T> &menu, const MPD::Song &s, BasicScreen &screen, bool
menu << fmtUnderline;
int song_pos = menu.isFiltered() ? s.getPosition() : menu.DrawnPosition();
is_now_playing = song_pos == myPlaylist->NowPlaying && s.getID() > 0; // playlist
is_now_playing = static_cast<void *>(&menu) == myPlaylist->Items
&& song_pos == myPlaylist->NowPlaying;
is_selected = menu.Drawn().isSelected();
discard_colors = Config.discard_colors_if_item_is_selected && is_selected;
}
template <typename T>
void showSongs(Menu<T> &menu, const MPD::Song &s, BasicScreen &screen, const std::string &format)
void showSongs(Menu<T> &menu, const MPD::Song &s, HasSongs &screen, const std::string &format)
{
bool separate_albums, is_now_playing, is_selected, discard_colors;
setProperties(menu, s, screen, separate_albums, is_now_playing, is_selected, discard_colors);
@@ -147,7 +148,7 @@ void showSongs(Menu<T> &menu, const MPD::Song &s, BasicScreen &screen, const std
}
template <typename T>
void showSongsInColumns(Menu<T> &menu, const MPD::Song &s, BasicScreen &screen)
void showSongsInColumns(Menu<T> &menu, const MPD::Song &s, HasSongs &screen)
{
if (Config.columns.empty())
return;
@@ -332,12 +333,12 @@ std::string Display::Columns(size_t list_width)
return result;
}
void Display::SongsInColumns(Menu<MPD::Song> &menu, BasicScreen &screen)
void Display::SongsInColumns(Menu<MPD::Song> &menu, HasSongs &screen)
{
showSongsInColumns(menu, menu.Drawn().value(), screen);
}
void Display::Songs(Menu<MPD::Song> &menu, BasicScreen &screen, const std::string &format)
void Display::Songs(Menu<MPD::Song> &menu, HasSongs &screen, const std::string &format)
{
showSongs(menu, menu.Drawn().value(), screen, format);
}

View File

@@ -21,11 +21,11 @@
#ifndef _DISPLAY_H
#define _DISPLAY_H
#include "interfaces.h"
#include "ncmpcpp.h"
#include "menu.h"
#include "mpdpp.h"
#include "mutable_song.h"
#include "screen.h"
#include "search_engine.h"
namespace Display
@@ -42,9 +42,9 @@ namespace Display
menu << menu.Drawn().value().first;
}
void SongsInColumns(Menu<MPD::Song> &menu, BasicScreen &screen);
void SongsInColumns(Menu<MPD::Song> &menu, HasSongs &screen);
void Songs(Menu<MPD::Song> &menu, BasicScreen &screen, const std::string &format);
void Songs(Menu<MPD::Song> &menu, HasSongs &screen, const std::string &format);
void Tags(Menu<MPD::MutableSong> &menu);

View File

@@ -270,11 +270,6 @@ void ParseArgv(int argc, char **argv)
exit(0);
}
std::string StringPairToString(const std::pair<std::string, std::string> &pair)
{
return pair.first;
}
std::string Timestamp(time_t t)
{
char result[32];
@@ -306,26 +301,6 @@ void UpdateSongList(Menu<MPD::Song> *menu)
menu->Refresh();
}
#ifdef HAVE_TAGLIB_H
std::string getSharedDirectory(const MPD::SongList &v)
{
if (v.empty()) // this should never happen, but in case...
FatalError("empty SongList passed to getSharedDirectory(const SongList &)!");
size_t i = -1;
std::string first = v.front().getDirectory();
for (MPD::SongList::const_iterator it = ++v.begin(); it != v.end(); ++it)
{
size_t j = 0;
std::string dir = it->getDirectory();
size_t length = std::min(first.length(), dir.length());
while (!first.compare(j, 1, dir, j, 1) && j < length && j < i)
++j;
i = j;
}
return i ? first.substr(0, i) : "/";
}
#endif // HAVE_TAGLIB_H
std::basic_string<my_char_t> Scroller(const std::basic_string<my_char_t> &str, size_t &pos, size_t width)
{
std::basic_string<my_char_t> s(str);

View File

@@ -26,9 +26,41 @@
#include "settings.h"
#include "status.h"
void ParseArgv(int, char **);
template <typename Iterator> void removeSelectionHelper(Iterator first, Iterator last)
{
for (; first != last; ++first)
first->setSelected(false);
}
std::string StringPairToString(const std::pair<std::string, std::string> &pair);
template <typename Iterator> void reverseSelectionHelper(Iterator first, Iterator last)
{
for (; first != last; ++first)
first->setSelected(!first->isSelected());
}
template <typename Iterator> std::string getSharedDirectory(Iterator first, Iterator last)
{
assert(first != last);
std::string result = first->getDirectory();
while (++first != last)
{
result = getSharedDirectory(result, first->getDirectory());
if (result == "/")
break;
}
return result;
}
template <typename T> void withUnfilteredMenu(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 {
const char *operator()(const char *s) { return s; }
@@ -172,22 +204,6 @@ std::string Timestamp(time_t t);
void UpdateSongList(Menu<MPD::Song> *);
#ifdef HAVE_TAGLIB_H
template <typename T> std::string getSharedDirectory(Menu<T> *menu)
{
assert(!menu->Empty());
std::string dir;
// dir = (*menu)[0].value().getDirectory();
for (size_t i = 1; i < menu->Size(); ++i)
{
dir = getSharedDirectory(dir, (*menu)[i].value().getDirectory());
if (dir == "/")
break;
}
return dir;
}
#endif // HAVE_TAGLIB_H
std::basic_string<my_char_t> Scroller(const std::basic_string<my_char_t> &str, size_t &pos, size_t width);
std::string Shorten(const std::basic_string<my_char_t> &s, size_t max_length);

View File

@@ -22,6 +22,7 @@
#define _INTERFACES_H
#include <string>
#include "mpdpp.h"
#include "gcc.h"
struct Filterable
@@ -37,4 +38,15 @@ struct Searchable
virtual void prevFound(bool wrap) = 0;
};
struct HasSongs
{
virtual MPD::Song *getSong(size_t pos) = 0;
virtual MPD::Song *currentSong() = 0;
virtual bool allowsSelection() = 0;
virtual void reverseSelection() = 0;
virtual void removeSelection() = 0;
virtual MPD::SongList getSelectedSongs() = 0;
};
#endif // _INTERFACES_H

View File

@@ -116,7 +116,11 @@ void Lyrics::SwitchTo()
}
# endif // HAVE_CURL_CURL_H
if (const MPD::Song *s = myScreen->CurrentSong())
auto hs = dynamic_cast<HasSongs *>(myScreen);
if (!hs)
return;
if (const MPD::Song *s = hs->currentSong())
{
if (!s->getArtist().empty() && !s->getTitle().empty())
{
@@ -137,7 +141,7 @@ void Lyrics::SwitchTo()
// if we resize for locked screen, we have to do that in the end since
// fetching lyrics may fail (eg. if tags are missing) and we don't want
// to adjust screen size then.
if (myLockedScreen)
if (myLockedScreen) // BUG
{
UpdateInactiveScreen(this);
Resize();

View File

@@ -19,6 +19,7 @@
***************************************************************************/
#include <algorithm>
#include <array>
#include <cassert>
#include "charset.h"
@@ -216,19 +217,16 @@ void MediaLibrary::Update()
{
if (!hasTwoColumns && Tags->ReallyEmpty())
{
MPD::TagList list;
Albums->Clear();
Songs->Clear();
Mpd.GetList(list, Config.media_lib_primary_tag);
sort(list.begin(), list.end(), CaseInsensitiveSorting());
for (MPD::TagList::iterator it = list.begin(); it != list.end(); ++it)
auto list = Mpd.GetList(Config.media_lib_primary_tag);
std::sort(list.begin(), list.end(), CaseInsensitiveSorting());
for (auto it = list.begin(); it != list.end(); ++it)
{
if (it->empty() && !Config.media_library_display_empty_tag)
continue;
utf_to_locale(*it);
Tags->AddItem(*it);
}
Tags->Window::Clear();
Tags->Refresh();
}
@@ -237,35 +235,25 @@ void MediaLibrary::Update()
// idle has to be blocked for now since it would be enabled and
// disabled a few times by each mpd command, which makes no sense
// and slows down the whole process.
Mpd.BlockIdle(1);
Mpd.BlockIdle(true);
Albums->Reset();
MPD::TagList list;
locale_to_utf(Tags->Current().value());
Mpd.StartFieldSearch(MPD_TAG_ALBUM);
Mpd.AddSearch(Config.media_lib_primary_tag, Tags->Current().value());
Mpd.CommitSearchTags([&list](std::string &&album) {
list.push_back(album);
});
for (auto album = list.begin(); album != list.end(); ++album)
auto albums = Mpd.CommitSearchTags();
for (auto album = albums.begin(); album != albums.end(); ++album)
{
if (Config.media_library_display_date)
{
Mpd.StartFieldSearch(MPD_TAG_DATE);
Mpd.AddSearch(Config.media_lib_primary_tag, Tags->Current().value());
Mpd.AddSearch(MPD_TAG_ALBUM, *album);
utf_to_locale(*album);
Mpd.CommitSearchTags([this, &album](std::string &&date) {
utf_to_locale(date);
Albums->AddItem(SearchConstraints(*album, date));
});
auto dates = Mpd.CommitSearchTags();
for (auto date = dates.begin(); date != dates.end(); ++date)
Albums->AddItem(SearchConstraints(*album, *date));
}
else
{
utf_to_locale(*album);
Albums->AddItem(SearchConstraints(*album, ""));
}
}
utf_to_locale(Tags->Current().value());
if (!Albums->Empty())
std::sort(Albums->BeginV(), Albums->EndV(), SortSearchConstraints);
if (Albums->Size() > 1)
@@ -274,24 +262,20 @@ void MediaLibrary::Update()
Albums->AddItem(SearchConstraints("", AllTracksMarker));
}
Albums->Refresh();
Mpd.BlockIdle(0);
Mpd.BlockIdle(false);
}
else if (hasTwoColumns && Albums->ReallyEmpty())
{
Songs->Clear();
MPD::TagList artists;
*Albums << XY(0, 0) << "Fetching albums...";
Albums->Window::Refresh();
Mpd.BlockIdle(1);
Mpd.GetList(artists, Config.media_lib_primary_tag);
Mpd.BlockIdle(true);
auto artists = Mpd.GetList(Config.media_lib_primary_tag);
for (auto artist = artists.begin(); artist != artists.end(); ++artist)
{
MPD::TagList albums;
Mpd.StartFieldSearch(MPD_TAG_ALBUM);
Mpd.AddSearch(Config.media_lib_primary_tag, *artist);
Mpd.CommitSearchTags([&albums](std::string &&album) {
albums.push_back(album);
});
auto albums = Mpd.CommitSearchTags();
for (auto album = albums.begin(); album != albums.end(); ++album)
{
if (Config.media_library_display_date)
@@ -301,26 +285,15 @@ void MediaLibrary::Update()
Mpd.StartFieldSearch(MPD_TAG_DATE);
Mpd.AddSearch(Config.media_lib_primary_tag, *artist);
Mpd.AddSearch(MPD_TAG_ALBUM, *album);
utf_to_locale(*artist);
utf_to_locale(*album);
Mpd.CommitSearchTags([this, &artist, &album](std::string &&tag) {
utf_to_locale(tag);
Albums->AddItem(SearchConstraints(*artist, *album, tag));
});
auto dates = Mpd.CommitSearchTags();
for (auto date = dates.begin(); date != dates.end(); ++date)
Albums->AddItem(SearchConstraints(*artist, *album, *date));
}
else
{
utf_to_locale(*artist);
utf_to_locale(*album);
Albums->AddItem(SearchConstraints(*artist, *album, *artist));
}
}
else
{
utf_to_locale(*artist);
utf_to_locale(*album);
Albums->AddItem(SearchConstraints(*artist, *album, ""));
}
}
}
Mpd.BlockIdle(0);
@@ -348,16 +321,15 @@ void MediaLibrary::Update()
if (Config.media_library_display_date)
Mpd.AddSearch(MPD_TAG_DATE, locale_to_utf_cpy(Albums->Current().value().Date));
}
Mpd.CommitSearchSongs([this](MPD::Song &&s) {
Songs->AddItem(s, myPlaylist->checkForSong(s));
});
auto songs = Mpd.CommitSearchSongs();
for (auto s = songs.begin(); s != songs.end(); ++s)
Songs->AddItem(*s, myPlaylist->checkForSong(*s));
if (Albums->Current().value().Date == AllTracksMarker)
std::sort(Songs->BeginV(), Songs->EndV(), SortAllTracks);
else
std::sort(Songs->BeginV(), Songs->EndV(), SortSongsByTrack);
Songs->Window::Clear();
Songs->Refresh();
}
}
@@ -462,11 +434,6 @@ void MediaLibrary::MouseButtonPressed(MEVENT me)
}
}
MPD::Song *MediaLibrary::CurrentSong()
{
return w == Songs && !Songs->Empty() ? &Songs->Current().value() : 0;
}
List *MediaLibrary::GetList()
{
if (w == Tags)
@@ -479,73 +446,6 @@ List *MediaLibrary::GetList()
return 0;
}
void MediaLibrary::ReverseSelection()
{
if (w == Tags)
Tags->ReverseSelection();
else if (w == Albums)
Albums->ReverseSelection();
else if (w == Songs)
Songs->ReverseSelection();
}
void MediaLibrary::GetSelectedSongs(MPD::SongList &v)
{
std::vector<size_t> selected;
if (w == Tags && !Tags->Empty())
{
Tags->GetSelected(selected);
if (selected.empty())
selected.push_back(Tags->Choice());
for (auto it = selected.begin(); it != selected.end(); ++it)
{
MPD::SongList list;
Mpd.StartSearch(1);
Mpd.AddSearch(Config.media_lib_primary_tag, locale_to_utf_cpy(Tags->at(*it).value()));
Mpd.CommitSearchSongs([&list](MPD::Song &&s) {
list.push_back(s);
});
std::sort(list.begin(), list.end(), SortAllTracks);
std::copy(list.begin(), list.end(), std::back_inserter(v));
}
}
else if (w == Albums && !Albums->Empty())
{
Albums->GetSelected(selected);
if (selected.empty())
{
// shortcut via the existing song list in right column
if (v.empty())
v.reserve(Songs->Size());
for (size_t i = 0; i < Songs->Size(); ++i)
v.push_back((*Songs)[i].value());
}
else
{
for (auto it = selected.begin(); it != selected.end(); ++it)
{
Mpd.StartSearch(1);
Mpd.AddSearch(Config.media_lib_primary_tag, hasTwoColumns
? Albums->at(*it).value().PrimaryTag
: locale_to_utf_cpy(Tags->Current().value()));
Mpd.AddSearch(MPD_TAG_ALBUM, Albums->at(*it).value().Album);
Mpd.AddSearch(MPD_TAG_DATE, Albums->at(*it).value().Date);
Mpd.CommitSearchSongs([&v](MPD::Song &&s) {
v.push_back(s);
});
}
}
}
else if (w == Songs && !Songs->Empty())
{
Songs->GetSelected(selected);
if (selected.empty())
selected.push_back(Songs->Choice());
for (auto it = selected.begin(); it != selected.end(); ++it)
v.push_back(Songs->at(*it).value());
}
}
/***********************************************************************/
std::string MediaLibrary::currentFilter()
@@ -629,6 +529,113 @@ void MediaLibrary::prevFound(bool wrap)
/***********************************************************************/
MPD::Song *MediaLibrary::getSong(size_t pos)
{
MPD::Song *ptr = 0;
if (w == Songs)
ptr = &(*Songs)[pos].value();
return ptr;
}
MPD::Song *MediaLibrary::currentSong()
{
if (w == Songs && !Songs->Empty())
return getSong(Songs->Choice());
else
return 0;
}
bool MediaLibrary::allowsSelection()
{
return true;
}
void MediaLibrary::removeSelection()
{
if (w == Tags)
removeSelectionHelper(Tags->Begin(), Tags->End());
else if (w == Albums)
removeSelectionHelper(Albums->Begin(), Albums->End());
else if (w == Songs)
removeSelectionHelper(Songs->Begin(), Songs->End());
}
void MediaLibrary::reverseSelection()
{
if (w == Tags)
reverseSelectionHelper(Tags->Begin(), Tags->End());
else if (w == Albums)
{
// omit "All tracks"
if (Albums->Size() > 1)
reverseSelectionHelper(Albums->Begin(), Albums->End()-2);
else
reverseSelectionHelper(Albums->Begin(), Albums->End());
}
else if (w == Songs)
reverseSelectionHelper(Songs->Begin(), Songs->End());
}
MPD::SongList MediaLibrary::getSelectedSongs()
{
MPD::SongList result;
if (w == Tags)
{
auto tag_handler = [&result](const std::string &tag) {
Mpd.StartSearch(true);
Mpd.AddSearch(Config.media_lib_primary_tag, tag);
auto songs = Mpd.CommitSearchSongs();
std::sort(songs.begin(), songs.end(), SortAllTracks);
result.insert(result.end(), songs.begin(), songs.end());
};
for (auto it = Tags->Begin(); it != Tags->End(); ++it)
if (it->isSelected())
tag_handler(it->value());
// if no item is selected, add current one
if (result.empty() && !Tags->Empty())
tag_handler(Tags->Current().value());
}
else if (w == Albums)
{
for (auto it = Albums->Begin(); it != Albums->End() && !it->isSeparator(); ++it)
{
if (it->isSelected())
{
auto &sc = it->value();
Mpd.StartSearch(true);
if (hasTwoColumns)
Mpd.AddSearch(Config.media_lib_primary_tag, sc.PrimaryTag);
else
Mpd.AddSearch(Config.media_lib_primary_tag, Tags->Current().value());
Mpd.AddSearch(MPD_TAG_ALBUM, sc.Album);
Mpd.AddSearch(MPD_TAG_DATE, sc.Date);
auto songs = Mpd.CommitSearchSongs();
std::sort(songs.begin(), songs.end(), SortSongsByTrack);
result.insert(result.end(), songs.begin(), songs.end());
}
}
// if no item is selected, add songs from right column
if (result.empty() && !Albums->Empty())
{
withUnfilteredMenu(*Songs, [this, &result]() {
result.insert(result.end(), Songs->BeginV(), Songs->EndV());
});
}
}
else if (w == Songs)
{
for (auto it = Songs->Begin(); it != Songs->End(); ++it)
if (it->isSelected())
result.push_back(it->value());
// if no item is selected, add current one
if (result.empty() && !Songs->Empty())
result.push_back(Songs->Current().value());
}
return result;
}
/***********************************************************************/
int MediaLibrary::Columns()
{
if (hasTwoColumns)
@@ -817,9 +824,7 @@ void MediaLibrary::AddToPlaylist(bool add_n_play)
}
else
{
MPD::SongList list;
GetSelectedSongs(list);
auto list = getSelectedSongs();
if (myPlaylist->Add(list, add_n_play))
{
if ((!Tags->Empty() && w == Tags)
@@ -915,20 +920,27 @@ void DisplayPrimaryTags(Menu<std::string> &menu)
bool SortSongsByTrack(const MPD::Song &a, const MPD::Song &b)
{
if (a.getDisc() == b.getDisc())
return stringToInt(a.getTrack()) < stringToInt(b.getTrack());
else
return stringToInt(a.getDisc()) < stringToInt(b.getDisc());
int cmp = a.getDisc().compare(a.getDisc());
if (cmp != 0)
return cmp;
return a.getTrack() < b.getTrack();
}
bool SortAllTracks(const MPD::Song &a, const MPD::Song &b)
{
static MPD::Song::GetFunction gets[] = { &MPD::Song::getDate, &MPD::Song::getAlbum, &MPD::Song::getDisc, 0 };
const std::array<MPD::Song::GetFunction, 3> gets = {{
&MPD::Song::getDate,
&MPD::Song::getAlbum,
&MPD::Song::getDisc
}};
CaseInsensitiveStringComparison cmp;
for (MPD::Song::GetFunction *get = gets; *get; ++get)
if (int ret = cmp(a.getTags(*get), b.getTags(*get)))
for (auto get = gets.begin(); get != gets.end(); ++get)
{
int ret = cmp(a.getTags(*get), b.getTags(*get));
if (ret != 0)
return ret < 0;
return a.getTrack() < b.getTrack();
}
return a.getTrack() < b.getTrack();
}
bool SortSearchConstraints(const SearchConstraints &a, const SearchConstraints &b)

View File

@@ -25,7 +25,7 @@
#include "ncmpcpp.h"
#include "screen.h"
class MediaLibrary : public Screen<Window>, public Filterable, public Searchable
class MediaLibrary : public Screen<Window>, public Filterable, public HasSongs, public Searchable
{
public:
virtual void SwitchTo();
@@ -41,13 +41,6 @@ class MediaLibrary : public Screen<Window>, public Filterable, public Searchable
virtual void MouseButtonPressed(MEVENT);
virtual bool isTabbable() { return true; }
virtual MPD::Song *CurrentSong();
virtual MPD::Song *GetSong(size_t pos) { return w == Songs ? &Songs->at(pos).value() : 0; }
virtual bool allowsSelection() { return true; }
virtual void ReverseSelection();
virtual void GetSelectedSongs(MPD::SongList &);
/// Filterable implementation
virtual std::string currentFilter();
virtual void applyFilter(const std::string &filter);
@@ -57,6 +50,15 @@ class MediaLibrary : public Screen<Window>, public Filterable, public Searchable
virtual void nextFound(bool wrap);
virtual void prevFound(bool wrap);
/// HasSongs implementation
virtual MPD::Song *getSong(size_t pos);
virtual MPD::Song *currentSong();
virtual bool allowsSelection();
virtual void reverseSelection();
virtual void removeSelection();
virtual MPD::SongList getSelectedSongs();
virtual List *GetList();
virtual bool isMergable() { return true; }

View File

@@ -1,19 +0,0 @@
/***************************************************************************
* Copyright (C) 2008-2012 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. *
***************************************************************************/

View File

@@ -153,7 +153,7 @@ template <typename T> struct Menu : public Window, public List
return ItemIterator<typename std::add_const<ValueT>::type, Iterator>(m_it);
}
const BaseIterator &base() { return m_it; }
const BaseIterator &base() const { return m_it; }
};
typedef ItemIterator<
@@ -251,10 +251,6 @@ template <typename T> struct Menu : public Window, public List
/// @param v vector to be filled with selected positions numbers
void GetSelected(std::vector<size_t> &v) const;
/// Reverses selection of all items in list
/// @param beginning beginning of range that has to be reversed
void ReverseSelection(size_t beginning = 0);
/// Highlights given position
/// @param pos position to be highlighted
void Highlight(size_t pos);
@@ -372,33 +368,27 @@ template <typename T> struct Menu : public Window, public List
const Menu<T>::Item &Back() const;
/// @return reference to curently highlighted object
/// @throw List::InvalidItem if requested item is separator
Menu<T>::Item &Current();
/// @return const reference to curently highlighted object
/// @throw List::InvalidItem if requested item is separator
const Menu<T>::Item &Current() const;
/// @param pos requested position
/// @return reference to item at given position
/// @throw std::out_of_range if given position is out of range
/// @throw List::InvalidItem if requested item is separator
Menu<T>::Item &at(size_t pos);
/// @param pos requested position
/// @return const reference to item at given position
/// @throw std::out_of_range if given position is out of range
/// @throw List::InvalidItem if requested item is separator
const Menu<T>::Item &at(size_t pos) const;
/// @param pos requested position
/// @return const reference to item at given position
/// @throw List::InvalidItem if requested item is separator
const Menu<T>::Item &operator[](size_t pos) const;
/// @param pos requested position
/// @return const reference to item at given position
/// @throw List::InvalidItem if requested item is separator
Menu<T>::Item &operator[](size_t pos);
Iterator Begin() { return Iterator(m_options_ptr->begin()); }
@@ -744,6 +734,7 @@ template <typename T> size_t Menu<T>::Size() const
template <typename T> size_t Menu<T>::Choice() const
{
assert(!Empty());
return m_highlight;
}
@@ -793,13 +784,6 @@ template <typename T> void Menu<T>::clearSearchResults()
m_found_positions.clear();
}
template <typename T> void Menu<T>::ReverseSelection(size_t beginning)
{
auto it = m_options_ptr->begin()+beginning;
for (size_t i = beginning; i < Size(); ++i, ++it)
(*it)->setSelected(!(*it)->isSelected() && !(*it)->isInactive());
}
template <typename T> void Menu<T>::NextFound(bool wrap)
{
if (m_found_positions.empty())

View File

@@ -621,17 +621,19 @@ bool MPD::Connection::Rename(const std::string &from, const std::string &to)
}
}
void MPD::Connection::GetPlaylistChanges(unsigned version, std::function<void(MPD::Song &&)> f)
MPD::SongList MPD::Connection::GetPlaylistChanges(unsigned version)
{
SongList result;
if (!itsConnection)
return;
return result;
assert(!isCommandsListEnabled);
GoBusy();
mpd_send_queue_changes_meta(itsConnection, version);
while (mpd_song *s = mpd_recv_song(itsConnection))
f(Song(s));
result.push_back(Song(s));
mpd_response_finish(itsConnection);
GoIdle();
return result;
}
MPD::Song MPD::Connection::GetSong(const std::string &path)
@@ -667,17 +669,19 @@ MPD::Song MPD::Connection::GetCurrentlyPlayingSong()
return result;
}
void MPD::Connection::GetPlaylistContent(const std::string &path, std::function<void(MPD::Song &&)> f)
MPD::SongList MPD::Connection::GetPlaylistContent(const std::string &path)
{
SongList result;
if (!itsConnection)
return;
return result;
assert(!isCommandsListEnabled);
GoBusy();
mpd_send_list_playlist_meta(itsConnection, path.c_str());
while (mpd_song *s = mpd_recv_song(itsConnection))
f(Song(s));
result.push_back(Song(s));
mpd_response_finish(itsConnection);
GoIdle();
return result;
}
void MPD::Connection::GetSupportedExtensions(std::set<std::string> &acc)
@@ -903,9 +907,7 @@ bool MPD::Connection::AddRandomTag(mpd_tag_type tag, size_t number)
return false;
assert(!isCommandsListEnabled);
TagList tags;
GetList(tags, tag);
auto tags = GetList(tag);
if (number > tags.size())
{
if (itsErrorHandler)
@@ -915,18 +917,15 @@ bool MPD::Connection::AddRandomTag(mpd_tag_type tag, size_t number)
else
{
std::random_shuffle(tags.begin(), tags.end());
TagList::const_iterator it = tags.begin()+rand()%(tags.size()-number);
auto it = tags.begin()+rand()%(tags.size()-number);
for (size_t i = 0; i < number && it != tags.end(); ++i)
{
StartSearch(1);
AddSearch(tag, *it++);
SongList list;
CommitSearchSongs([&list](MPD::Song &&s) {
list.push_back(s);
});
auto songs = CommitSearchSongs();
StartCommandsList();
for (auto j = list.begin(); j != list.end(); ++j)
AddSong(*j);
for (auto s = songs.begin(); s != songs.end(); ++s)
AddSong(*s);
CommitCommandsList();
}
}
@@ -939,7 +938,7 @@ bool MPD::Connection::AddRandomSongs(size_t number)
return false;
assert(!isCommandsListEnabled);
TagList files;
StringList files;
GoBusy();
mpd_send_list_all(itsConnection, "/");
@@ -958,12 +957,11 @@ bool MPD::Connection::AddRandomSongs(size_t number)
}
else
{
srand(time(0));
std::random_shuffle(files.begin(), files.end());
StartCommandsList();
TagList::const_iterator it = files.begin()+rand()%(std::max(size_t(1), files.size()-number));
for (size_t i = 0; i < number && it != files.end(); ++i)
AddSong(*it++);
auto it = files.begin()+rand()%(std::max(size_t(1), files.size()-number));
for (size_t i = 0; i < number && it != files.end(); ++i, ++it)
AddSong(*it);
CommitCommandsList();
}
return true;
@@ -1075,31 +1073,35 @@ int MPD::Connection::SavePlaylist(const std::string &name)
return CheckForErrors();
}
void MPD::Connection::GetPlaylists(TagList &v)
MPD::StringList MPD::Connection::GetPlaylists()
{
StringList result;
if (!itsConnection)
return;
GetDirectory("/", [&v](Item &&it) {
if (it.type == itPlaylist)
v.push_back(it.name);
});
return result;
auto items = GetDirectory("/");
for (auto it = items.begin(); it != items.end(); ++it)
if (it->type == itPlaylist)
result.push_back(it->name);
return result;
}
void MPD::Connection::GetList(TagList &v, mpd_tag_type type)
MPD::StringList MPD::Connection::GetList(mpd_tag_type type)
{
StringList result;
if (!itsConnection)
return;
return result;
assert(!isCommandsListEnabled);
GoBusy();
mpd_search_db_tags(itsConnection, type);
mpd_search_commit(itsConnection);
while (mpd_pair *item = mpd_recv_pair_tag(itsConnection, type))
{
v.push_back(item->value);
result.push_back(item->value);
mpd_return_pair(itsConnection, item);
}
mpd_response_finish(itsConnection);
GoIdle();
return result;
}
void MPD::Connection::StartSearch(bool exact_match)
@@ -1140,39 +1142,44 @@ void MPD::Connection::AddSearchURI(const std::string &str) const
mpd_search_add_uri_constraint(itsConnection, MPD_OPERATOR_DEFAULT, str.c_str());
}
void MPD::Connection::CommitSearchSongs(std::function<void(MPD::Song &&)> f)
MPD::SongList MPD::Connection::CommitSearchSongs()
{
SongList result;
if (!itsConnection)
return;
return result;
assert(!isCommandsListEnabled);
GoBusy();
mpd_search_commit(itsConnection);
while (mpd_song *s = mpd_recv_song(itsConnection))
f(Song(s));
result.push_back(Song(s));
mpd_response_finish(itsConnection);
GoIdle();
return result;
}
void MPD::Connection::CommitSearchTags(std::function<void(std::string &&)> f)
MPD::StringList MPD::Connection::CommitSearchTags()
{
StringList result;
if (!itsConnection)
return;
return result;
assert(!isCommandsListEnabled);
GoBusy();
mpd_search_commit(itsConnection);
while (mpd_pair *tag = mpd_recv_pair_tag(itsConnection, itsSearchedField))
{
f(tag->value);
result.push_back(tag->value);
mpd_return_pair(itsConnection, tag);
}
mpd_response_finish(itsConnection);
GoIdle();
return result;
}
void MPD::Connection::GetDirectory(const std::string &path, std::function<void(Item &&)> f)
MPD::ItemList MPD::Connection::GetDirectory(const std::string &path)
{
ItemList result;
if (!itsConnection)
return;
return result;
assert(!isCommandsListEnabled);
GoBusy();
mpd_send_list_meta(itsConnection, path.c_str());
@@ -1197,68 +1204,77 @@ void MPD::Connection::GetDirectory(const std::string &path, std::function<void(I
assert(false);
}
mpd_entity_free(item);
f(std::move(it));
result.push_back(std::move(it));
}
mpd_response_finish(itsConnection);
GoIdle();
return result;
}
void MPD::Connection::GetDirectoryRecursive(const std::string &path, std::function<void(MPD::Song &&)> f)
MPD::SongList MPD::Connection::GetDirectoryRecursive(const std::string &path)
{
SongList result;
if (!itsConnection)
return;
return result;
assert(!isCommandsListEnabled);
GoBusy();
mpd_send_list_all_meta(itsConnection, path.c_str());
while (mpd_song *s = mpd_recv_song(itsConnection))
f(Song(s));
result.push_back(Song(s));
mpd_response_finish(itsConnection);
GoIdle();
return result;
}
void MPD::Connection::GetDirectories(const std::string &path, TagList &v)
MPD::StringList MPD::Connection::GetDirectories(const std::string &path)
{
StringList result;
if (!itsConnection)
return;
return result;
assert(!isCommandsListEnabled);
GoBusy();
mpd_send_list_meta(itsConnection, path.c_str());
while (mpd_directory *dir = mpd_recv_directory(itsConnection))
{
v.push_back(mpd_directory_get_path(dir));
result.push_back(mpd_directory_get_path(dir));
mpd_directory_free(dir);
}
mpd_response_finish(itsConnection);
GoIdle();
return result;
}
void MPD::Connection::GetSongs(const std::string &path, SongList &v)
MPD::SongList MPD::Connection::GetSongs(const std::string &path)
{
SongList result;
if (!itsConnection)
return;
return result;
assert(!isCommandsListEnabled);
GoBusy();
mpd_send_list_meta(itsConnection, path.c_str());
while (mpd_song *s = mpd_recv_song(itsConnection))
v.push_back(Song(s));
result.push_back(Song(s));
mpd_response_finish(itsConnection);
GoIdle();
return result;
}
void MPD::Connection::GetOutputs(std::function<void(Output &&)> f)
MPD::OutputList MPD::Connection::GetOutputs()
{
OutputList result;
if (!itsConnection)
return;
return result;
assert(!isCommandsListEnabled);
GoBusy();
mpd_send_outputs(itsConnection);
while (mpd_output *output = mpd_recv_output(itsConnection))
{
f(Output(mpd_output_get_name(output), mpd_output_get_enabled(output)));
result.push_back(Output(mpd_output_get_name(output), mpd_output_get_enabled(output)));
mpd_output_free(output);
}
mpd_response_finish(itsConnection);
GoIdle();
return result;
}
bool MPD::Connection::EnableOutput(int id)
@@ -1293,36 +1309,40 @@ bool MPD::Connection::DisableOutput(int id)
}
}
void MPD::Connection::GetURLHandlers(std::function<void(std::string &&)> f)
MPD::StringList MPD::Connection::GetURLHandlers()
{
StringList result;
if (!itsConnection)
return;
return result;
assert(!isCommandsListEnabled);
GoBusy();
mpd_send_list_url_schemes(itsConnection);
while (mpd_pair *handler = mpd_recv_pair_named(itsConnection, "handler"))
{
f(handler->value);
result.push_back(handler->value);
mpd_return_pair(itsConnection, handler);
}
mpd_response_finish(itsConnection);
GoIdle();
return result;
}
void MPD::Connection::GetTagTypes(std::function<void(std::string &&)> f)
MPD::StringList MPD::Connection::GetTagTypes()
{
StringList result;
if (!itsConnection)
return;
return result;
assert(!isCommandsListEnabled);
GoBusy();
mpd_send_list_tag_types(itsConnection);
while (mpd_pair *tag_type = mpd_recv_pair_named(itsConnection, "tagtype"))
{
f(tag_type->value);
result.push_back(tag_type->value);
mpd_return_pair(itsConnection, tag_type);
}
mpd_response_finish(itsConnection);
GoIdle();
return result;
}
int MPD::Connection::CheckForErrors()

View File

@@ -74,7 +74,7 @@ namespace MPD
typedef std::vector<Item> ItemList;
typedef std::vector<Song> SongList;
typedef std::vector<std::string> TagList;
typedef std::vector<std::string> StringList;
typedef std::vector<Output> OutputList;
class Connection
@@ -153,15 +153,15 @@ namespace MPD
unsigned long DBPlayTime() const { return itsStats ? mpd_stats_get_db_play_time(itsStats) : 0; }
size_t GetPlaylistLength() const { return itsCurrentStatus ? mpd_status_get_queue_length(itsCurrentStatus) : 0; }
void GetPlaylistChanges(unsigned, std::function<void(Song &&)> f);
SongList GetPlaylistChanges(unsigned);
const std::string & GetErrorMessage() const { return itsErrorMessage; }
const std::string &GetErrorMessage() const { return itsErrorMessage; }
Song GetCurrentlyPlayingSong();
int GetCurrentlyPlayingSongPos() const;
int GetCurrentSongPos() const;
Song GetSong(const std::string &);
void GetPlaylistContent(const std::string &, std::function<void(Song &&)> f);
SongList GetPlaylistContent(const std::string &);
void GetSupportedExtensions(std::set<std::string> &);
@@ -202,22 +202,22 @@ namespace MPD
void AddSearch(mpd_tag_type, const std::string &) const;
void AddSearchAny(const std::string &str) const;
void AddSearchURI(const std::string &str) const;
void CommitSearchSongs(std::function<void(Song &&)> f);
void CommitSearchTags(std::function<void(std::string &&)> f);
SongList CommitSearchSongs();
StringList CommitSearchTags();
void GetPlaylists(TagList &);
void GetList(TagList &, mpd_tag_type);
void GetDirectory(const std::string &, std::function<void(Item &&)> f);
void GetDirectoryRecursive(const std::string &, std::function<void(Song &&)> f);
void GetSongs(const std::string &, SongList &);
void GetDirectories(const std::string &, TagList &);
StringList GetPlaylists();
StringList GetList(mpd_tag_type);
ItemList GetDirectory(const std::string &);
SongList GetDirectoryRecursive(const std::string &);
SongList GetSongs(const std::string &);
StringList GetDirectories(const std::string &);
void GetOutputs(std::function<void(Output &&)> f);
OutputList GetOutputs();
bool EnableOutput(int);
bool DisableOutput(int);
void GetURLHandlers(std::function<void(std::string &&)> f);
void GetTagTypes(std::function<void(std::string &&)> f);
StringList GetURLHandlers();
StringList GetTagTypes();
private:
void GoIdle();

View File

@@ -116,9 +116,9 @@ void Outputs::FetchList()
if (!isInitialized)
return;
w->Clear();
Mpd.GetOutputs([this](MPD::Output &&o) {
w->AddItem(o, o.isEnabled());
});
auto outputs = Mpd.GetOutputs();
for (auto o = outputs.begin(); o != outputs.end(); ++o)
w->AddItem(*o, o->isEnabled());
if (myScreen == this)
w->Refresh();
}

View File

@@ -269,23 +269,6 @@ void Playlist::MouseButtonPressed(MEVENT me)
}
}
MPD::Song *Playlist::CurrentSong()
{
return w == Items && !Items->Empty() ? &Items->Current().value() : 0;
}
void Playlist::GetSelectedSongs(MPD::SongList &v)
{
if (Items->Empty())
return;
std::vector<size_t> selected;
Items->GetSelected(selected);
if (selected.empty())
selected.push_back(Items->Choice());
for (auto it = selected.begin(); it != selected.end(); ++it)
v.push_back(Items->at(*it).value());
}
/***********************************************************************/
std::string Playlist::currentFilter()
@@ -333,6 +316,54 @@ void Playlist::prevFound(bool wrap)
/***********************************************************************/
MPD::Song *Playlist::getSong(size_t pos)
{
MPD::Song *ptr = 0;
if (w == Items)
ptr = &(*Items)[pos].value();
return ptr;
}
MPD::Song *Playlist::currentSong()
{
if (Items->Empty())
return 0;
else
return getSong(Items->Choice());
}
bool Playlist::allowsSelection()
{
return w == Items;
}
void Playlist::removeSelection()
{
removeSelectionHelper(Items->Begin(), Items->End());
}
void Playlist::reverseSelection()
{
reverseSelectionHelper(Items->Begin(), Items->End());
}
MPD::SongList Playlist::getSelectedSongs()
{
MPD::SongList result;
if (w == Items)
{
for (auto it = Items->Begin(); it != Items->End(); ++it)
if (it->isSelected())
result.push_back(it->value());
if (result.empty() && !Items->Empty())
result.push_back(Items->Current().value());
}
return result;
}
/***********************************************************************/
bool Playlist::isFiltered()
{
if (Items->isFiltered())

View File

@@ -26,7 +26,7 @@
#include "screen.h"
#include "song.h"
class Playlist : public Screen<Window>, public Filterable, public Searchable
class Playlist : public Screen<Window>, public Filterable, public HasSongs, public Searchable
{
public:
enum Movement { mUp, mDown };
@@ -44,13 +44,6 @@ class Playlist : public Screen<Window>, public Filterable, public Searchable
virtual void MouseButtonPressed(MEVENT);
virtual bool isTabbable() { return true; }
virtual MPD::Song *CurrentSong();
virtual MPD::Song *GetSong(size_t pos) { return w == Items ? &Items->at(pos).value() : 0; }
virtual bool allowsSelection() { return w == Items; }
virtual void ReverseSelection() { Items->ReverseSelection(); }
virtual void GetSelectedSongs(MPD::SongList &);
/// Filterable implementation
virtual std::string currentFilter();
virtual void applyFilter(const std::string &filter);
@@ -60,6 +53,15 @@ class Playlist : public Screen<Window>, public Filterable, public Searchable
virtual void nextFound(bool wrap);
virtual void prevFound(bool wrap);
/// HasSongs implementation
virtual MPD::Song *getSong(size_t pos);
virtual MPD::Song *currentSong();
virtual bool allowsSelection();
virtual void reverseSelection();
virtual void removeSelection();
virtual MPD::SongList getSelectedSongs();
virtual List *GetList() { return w == Items ? Items : 0; }
virtual bool isMergable() { return true; }

View File

@@ -60,9 +60,11 @@ void PlaylistEditor::Init()
Playlists->HighlightColor(Config.active_column_color);
Playlists->CyclicScrolling(Config.use_cyclic_scrolling);
Playlists->CenteredCursor(Config.centered_cursor);
Playlists->SetSelectPrefix(Config.selected_item_prefix);
Playlists->SetSelectSuffix(Config.selected_item_suffix);
Playlists->setItemDisplayer(Display::Default<std::string>);
Content = new Menu<MPD::Song>(RightColumnStartX, MainStartY, RightColumnWidth, MainHeight, Config.titles_visibility ? "Playlist's content" : "", Config.main_color, brNone);
Content = new Menu<MPD::Song>(RightColumnStartX, MainStartY, RightColumnWidth, MainHeight, Config.titles_visibility ? "Playlist content" : "", Config.main_color, brNone);
Content->HighlightColor(Config.main_highlight_color);
Content->CyclicScrolling(Config.use_cyclic_scrolling);
Content->CenteredCursor(Config.centered_cursor);
@@ -138,15 +140,10 @@ void PlaylistEditor::Update()
if (Playlists->ReallyEmpty())
{
Content->Clear();
MPD::TagList list;
Mpd.GetPlaylists(list);
sort(list.begin(), list.end(), CaseInsensitiveSorting());
for (MPD::TagList::iterator it = list.begin(); it != list.end(); ++it)
{
utf_to_locale(*it);
auto list = Mpd.GetPlaylists();
std::sort(list.begin(), list.end(), CaseInsensitiveSorting());
for (auto it = list.begin(); it != list.end(); ++it)
Playlists->AddItem(*it);
}
Playlists->Window::Clear();
Playlists->Refresh();
}
@@ -154,20 +151,26 @@ void PlaylistEditor::Update()
{
Content->Reset();
size_t plsize = 0;
Mpd.GetPlaylistContent(locale_to_utf_cpy(Playlists->Current().value()), [this, &plsize](MPD::Song &&s) {
Content->AddItem(s, myPlaylist->checkForSong(s));
++plsize;
});
if (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)
{
std::string title = Config.titles_visibility ? "Playlist content (" + unsignedLongIntTo<std::string>::apply(plsize) + " item" + (plsize == 1 ? ")" : "s)") : "";
title = "Playlist content";
if (plsize > 0)
{
title += " (";
title += unsignedLongIntTo<std::string>::apply(plsize);
title += " item";
if (plsize == 1)
title += ")";
else
title += "s)";
}
title.resize(Content->GetWidth());
Content->SetTitle(title);
}
else
Content->SetTitle(Config.titles_visibility ? "Playlist content" : "");
Content->Window::Clear();
Content->SetTitle(title);
Content->Display();
}
@@ -351,13 +354,27 @@ void PlaylistEditor::AddToPlaylist(bool add_n_play)
void PlaylistEditor::SpacePressed()
{
if (Config.space_selects && w == Content)
if (Config.space_selects)
{
Content->Current().setSelected(!Content->Current().isSelected());
w->Scroll(wDown);
if (w == Playlists)
{
if (!Playlists->Empty())
{
Playlists->Current().setSelected(!Playlists->Current().isSelected());
Playlists->Scroll(wDown);
}
}
else if (w == Content)
{
if (!Content->Empty())
{
Content->Current().setSelected(!Content->Current().isSelected());
Content->Scroll(wDown);
}
}
}
else
AddToPlaylist(0);
AddToPlaylist(false);
}
void PlaylistEditor::MouseButtonPressed(MEVENT me)
@@ -403,21 +420,6 @@ void PlaylistEditor::MouseButtonPressed(MEVENT me)
}
}
MPD::Song *PlaylistEditor::CurrentSong()
{
return w == Content && !Content->Empty() ? &Content->Current().value() : 0;
}
void PlaylistEditor::GetSelectedSongs(MPD::SongList &v)
{
std::vector<size_t> selected;
Content->GetSelected(selected);
if (selected.empty())
selected.push_back(Content->Choice());
for (auto it = selected.begin(); it != selected.end(); ++it)
v.push_back(Content->at(*it).value());
}
/***********************************************************************/
std::string PlaylistEditor::currentFilter()
@@ -482,6 +484,81 @@ void PlaylistEditor::prevFound(bool wrap)
/***********************************************************************/
MPD::Song *PlaylistEditor::getSong(size_t pos)
{
MPD::Song *ptr = 0;
if (w == Content)
ptr = &(*Content)[pos].value();
return ptr;
}
MPD::Song *PlaylistEditor::currentSong()
{
if (w == Content && !Content->Empty())
return getSong(Content->Choice());
else
return 0;
}
bool PlaylistEditor::allowsSelection()
{
return true;
}
void PlaylistEditor::removeSelection()
{
if (w == Playlists)
removeSelectionHelper(Playlists->Begin(), Playlists->End());
else if (w == Content)
removeSelectionHelper(Content->Begin(), Content->End());
}
void PlaylistEditor::reverseSelection()
{
if (w == Playlists)
reverseSelectionHelper(Playlists->Begin(), Playlists->End());
else if (w == Content)
reverseSelectionHelper(Content->Begin(), Content->End());
}
MPD::SongList PlaylistEditor::getSelectedSongs()
{
MPD::SongList result;
if (w == Playlists)
{
bool any_selected = false;
for (auto it = Playlists->Begin(); it != Playlists->End(); ++it)
{
if (it->isSelected())
{
any_selected = true;
auto songs = Mpd.GetPlaylistContent(it->value());
result.insert(result.end(), songs.begin(), songs.end());
}
}
// we don't check for empty result here as it's possible that
// all selected playlists are empty.
if (!any_selected && !Content->Empty())
{
withUnfilteredMenu(*Content, [this, &result]() {
result.insert(result.end(), Content->BeginV(), Content->EndV());
});
}
}
else if (w == Content)
{
for (auto it = Content->Begin(); it != Content->End(); ++it)
if (it->isSelected())
result.push_back(it->value());
// if no item is selected, add current one
if (result.empty() && !Content->Empty())
result.push_back(Content->Current().value());
}
return result;
}
/***********************************************************************/
void PlaylistEditor::Locate(const std::string &name)
{
if (!isInitialized)

View File

@@ -24,7 +24,7 @@
#include "playlist.h"
#include "ncmpcpp.h"
class PlaylistEditor : public Screen<Window>, public Filterable, public Searchable
class PlaylistEditor : public Screen<Window>, public Filterable, public HasSongs, public Searchable
{
public:
virtual void SwitchTo();
@@ -40,13 +40,6 @@ class PlaylistEditor : public Screen<Window>, public Filterable, public Searchab
virtual void MouseButtonPressed(MEVENT);
virtual bool isTabbable() { return true; }
virtual MPD::Song *CurrentSong();
virtual MPD::Song *GetSong(size_t pos) { return w == Content ? &Content->at(pos).value() : 0; }
virtual bool allowsSelection() { return w == Content; }
virtual void ReverseSelection() { Content->ReverseSelection(); }
virtual void GetSelectedSongs(MPD::SongList &);
/// Filterable implementation
virtual std::string currentFilter();
virtual void applyFilter(const std::string &filter);
@@ -56,6 +49,15 @@ class PlaylistEditor : public Screen<Window>, public Filterable, public Searchab
virtual void nextFound(bool wrap);
virtual void prevFound(bool wrap);
/// HasSongs implementation
virtual MPD::Song *getSong(size_t pos);
virtual MPD::Song *currentSong();
virtual bool allowsSelection();
virtual void reverseSelection();
virtual void removeSelection();
virtual MPD::SongList getSelectedSongs();
virtual void Locate(const std::string &);
virtual List *GetList();

View File

@@ -42,6 +42,16 @@ Regex::~Regex()
regfree(&m_rx);
}
const std::string &Regex::regex() const
{
return m_regex;
}
const std::string &Regex::error() const
{
return m_error;
}
bool Regex::compile()
{
if (m_compiled)

View File

@@ -32,10 +32,10 @@ struct Regex
virtual ~Regex();
/// @return regular expression
const std::string &regex() const { return m_regex; }
const std::string &regex() const;
/// @return compilation error (if there was any)
const std::string &error() const { return m_error; }
const std::string &error() const;
/// compiles regular expression
/// @result true if compilation was successful, false otherwise
@@ -59,4 +59,4 @@ private:
bool m_compiled;
};
#endif // _REGEXES_H
#endif // _REGEXES_H

View File

@@ -46,84 +46,46 @@ class BasicScreen
virtual ~BasicScreen() { }
/// @see Screen::ActiveWindow()
///
virtual Window *ActiveWindow() = 0;
/// Method used for switching to screen
///
virtual void SwitchTo() = 0;
/// Method that should resize screen
/// if requested by hasToBeResized
///
virtual void Resize() = 0;
/// @return title of the screen
///
virtual std::basic_string<my_char_t> Title() = 0;
/// If the screen contantly has to update itself
/// somehow, it should be called by this function.
///
virtual void Update() { }
/// @see Screen::Refresh()
///
virtual void Refresh() = 0;
/// @see Screen::RefreshWindow()
///
virtual void RefreshWindow() = 0;
/// @see Screen::Scroll()
///
virtual void Scroll(Where where) = 0;
/// Invoked after Enter was pressed
///
virtual void EnterPressed() = 0;
/// Invoked after Space was pressed
///
virtual void SpacePressed() = 0;
/// @see Screen::MouseButtonPressed()
///
virtual void MouseButtonPressed(MEVENT) { }
/// @return pointer to currently selected song in the screen
/// (if screen provides one) or null pointer otherwise.
///
virtual MPD::Song *CurrentSong() { return 0; }
/// @return pointer to song at given position in the screen
/// (if screen is provides one) or null pointer otherwise.
///
virtual MPD::Song *GetSong(GNUC_UNUSED size_t pos) { return 0; }
/// @return true if the screen allows selecting items, false otherwise
///
virtual bool allowsSelection() = 0;
/// Reverses selection. Does nothing by default since pure
/// virtual allowsSelection() should remind of this function
/// to be defined
///
virtual void ReverseSelection() { }
/// Gets selected songs' positions from the screen
/// @param v vector to be filled with positions
///
virtual void GetSelectedSongs(GNUC_UNUSED MPD::SongList &v) { }
/// @return pointer to instantiation of Menu template class
/// cast to List if available or null pointer otherwise
///
virtual List *GetList() = 0;
/// When this is overwritten with a function returning true, the
/// screen will be used in tab switching.
///
virtual bool isTabbable() { return false; }
/// @return true if screen is mergable, ie. can be "proper" subwindow
@@ -137,7 +99,6 @@ class BasicScreen
bool Lock();
/// Should be set to true each time screen needs resize
///
bool hasToBeResized;
/// Unlocks a screen, ie. hides merged window (if there is one set).
@@ -182,33 +143,26 @@ template <typename WindowType> class Screen : public BasicScreen
/// it's useful to determine the one that is being
/// active
/// @return address to window object cast to void *
///
virtual Window *ActiveWindow();
/// @return pointer to currently active window
///
WindowType *Main();
/// Refreshes whole screen
///
virtual void Refresh();
/// Refreshes active window of the screen
///
virtual void RefreshWindow();
/// Scrolls the screen by given amount of lines and
/// if fancy scrolling feature is disabled, enters the
/// loop that holds main loop until user releases the key
/// @param where indicates where one wants to scroll
///
virtual void Scroll(Where where);
/// Invoked after there was one of mouse buttons pressed
/// @param me struct that contains coords of where the click
/// had its place and button actions
///
virtual void MouseButtonPressed(MEVENT me);
protected:
@@ -216,7 +170,6 @@ template <typename WindowType> class Screen : public BasicScreen
/// of window used by the screen. What is more, it should
/// always be assigned to the currently active window (if
/// acreen contains more that one)
///
WindowType *w;
};
@@ -268,7 +221,6 @@ template <typename WindowType> void Screen<WindowType>::MouseButtonPressed(MEVEN
/// Specialization for Screen<Scrollpad>::MouseButtonPressed, that should
/// not scroll whole page, but rather a few lines (the number of them is
/// defined in the config)
///
template <> inline void Screen<Scrollpad>::MouseButtonPressed(MEVENT me)
{
if (me.bstate & BUTTON2_PRESSED)

View File

@@ -276,26 +276,6 @@ void SearchEngine::MouseButtonPressed(MEVENT me)
Screen< Menu<SEItem> >::MouseButtonPressed(me);
}
MPD::Song *SearchEngine::CurrentSong()
{
return !w->Empty() && w->Current().value().isSong() ? &w->Current().value().song() : 0;
}
void SearchEngine::GetSelectedSongs(MPD::SongList &v)
{
if (w->Empty())
return;
std::vector<size_t> selected;
w->GetSelected(selected);
if (selected.empty() && w->Choice() >= StaticOptions)
selected.push_back(w->Choice());
for (auto it = selected.begin(); it != selected.end(); ++it)
{
assert(w->at(*it).value().isSong());
v.push_back(w->at(*it).value().song());
}
}
/***********************************************************************/
std::string SearchEngine::currentFilter()
@@ -332,22 +312,65 @@ void SearchEngine::prevFound(bool wrap)
/***********************************************************************/
MPD::Song *SearchEngine::getSong(size_t pos)
{
MPD::Song *ptr = 0;
auto &item = (*w)[pos];
if (!item.isSeparator() && item.value().isSong())
ptr = &item.value().song();
return ptr;
}
MPD::Song *SearchEngine::currentSong()
{
if (w->Empty())
return 0;
else
return getSong(w->Choice());
}
bool SearchEngine::allowsSelection()
{
return w->Current().value().isSong();
}
void SearchEngine::removeSelection()
{
removeSelectionHelper(w->Begin(), w->End());
}
void SearchEngine::reverseSelection()
{
reverseSelectionHelper(w->Begin()+std::min(StaticOptions, w->Size()), w->End());
}
MPD::SongList SearchEngine::getSelectedSongs()
{
MPD::SongList result;
for (auto it = w->Begin(); it != w->End(); ++it)
{
if (it->isSelected())
{
assert(it->value().isSong());
result.push_back(it->value().song());
}
}
// if no item is selected, add current one
if (result.empty() && !w->Empty())
{
assert(w->Current().value().isSong());
result.push_back(w->Current().value().song());
}
return result;
}
/***********************************************************************/
void SearchEngine::UpdateFoundList()
{
bool bold = 0;
for (size_t i = StaticOptions; i < w->Size(); ++i)
{
for (size_t j = 0; j < myPlaylist->Items->Size(); ++j)
{
if (myPlaylist->Items->at(j).value().getHash() == w->at(i).value().song().getHash())
{
bold = 1;
break;
}
}
w->at(i).setBold(bold);
bold = 0;
}
for (auto it = w->Begin(); it != w->End(); ++it)
if (it->value().isSong())
it->setBold(myPlaylist->checkForSong(it->value().song()));
}
void SearchEngine::Prepare()
@@ -422,30 +445,26 @@ void SearchEngine::Search()
Mpd.AddSearch(MPD_TAG_DATE, itsConstraints[9]);
if (!itsConstraints[10].empty())
Mpd.AddSearch(MPD_TAG_COMMENT, itsConstraints[10]);
Mpd.CommitSearchSongs([this](MPD::Song &&s) {
w->AddItem(s);
});
auto songs = Mpd.CommitSearchSongs();
for (auto s = songs.begin(); s != songs.end(); ++s)
w->AddItem(*s);
return;
}
MPD::SongList list;
if (Config.search_in_db)
{
Mpd.GetDirectoryRecursive("/", [&list](MPD::Song &&s) {
list.push_back(s);
});
}
list = Mpd.GetDirectoryRecursive("/");
else
{
list.reserve(myPlaylist->Items->Size());
for (size_t i = 0; i < myPlaylist->Items->Size(); ++i)
list.push_back((*myPlaylist->Items)[i].value());
for (auto s = myPlaylist->Items->BeginV(); s != myPlaylist->Items->EndV(); ++s)
list.push_back(*s);
}
bool any_found = 1;
bool found = 1;
for (MPD::SongList::const_iterator it = list.begin(); it != list.end(); ++it)
for (auto it = list.begin(); it != list.end(); ++it)
{
if (SearchMode != &SearchModes[2]) // match to pattern
{

View File

@@ -26,6 +26,7 @@
#include "interfaces.h"
#include "mpdpp.h"
#include "ncmpcpp.h"
#include "screen.h"
struct SEItem
{
@@ -73,7 +74,7 @@ struct SEItem
MPD::Song itsSong;
};
class SearchEngine : public Screen< Menu<SEItem> >, public Filterable, public Searchable
class SearchEngine : public Screen< Menu<SEItem> >, public Filterable, public HasSongs, public Searchable
{
public:
virtual void Resize();
@@ -86,13 +87,6 @@ class SearchEngine : public Screen< Menu<SEItem> >, public Filterable, public Se
virtual void MouseButtonPressed(MEVENT);
virtual bool isTabbable() { return true; }
virtual MPD::Song *CurrentSong();
virtual MPD::Song *GetSong(size_t pos) { return !(*w)[pos].isSeparator() && w->at(pos).value().isSong() ? &w->at(pos).value().song() : 0; }
virtual bool allowsSelection() { return w->Choice() >= StaticOptions; }
virtual void ReverseSelection() { w->ReverseSelection(StaticOptions); }
virtual void GetSelectedSongs(MPD::SongList &);
/// Filterable implementation
virtual std::string currentFilter();
virtual void applyFilter(const std::string &filter);
@@ -102,6 +96,15 @@ class SearchEngine : public Screen< Menu<SEItem> >, public Filterable, public Se
virtual void nextFound(bool wrap);
virtual void prevFound(bool wrap);
/// HasSongs implementation
virtual MPD::Song *getSong(size_t pos);
virtual MPD::Song *currentSong();
virtual bool allowsSelection();
virtual void reverseSelection();
virtual void removeSelection();
virtual MPD::SongList getSelectedSongs();
virtual List *GetList() { return w->Size() >= StaticOptions ? w : 0; }
virtual bool isMergable() { return true; }

View File

@@ -69,7 +69,8 @@ void SelectedItemsAdder::SwitchTo()
myOldScreen->SwitchTo();
return;
}
if (!myScreen->allowsSelection())
auto hs = dynamic_cast<HasSongs *>(myScreen);
if (!hs || !hs->allowsSelection())
return;
if (MainHeight < 5)
@@ -101,19 +102,14 @@ void SelectedItemsAdder::SwitchTo()
w->AddItem("New playlist", 0, playlists_not_active);
w->AddSeparator();
MPD::TagList playlists;
Mpd.GetPlaylists(playlists);
auto playlists = Mpd.GetPlaylists();
std::sort(playlists.begin(), playlists.end(), CaseInsensitiveSorting());
for (MPD::TagList::iterator it = playlists.begin(); it != playlists.end(); ++it)
{
utf_to_locale(*it);
for (auto it = playlists.begin(); it != playlists.end(); ++it)
w->AddItem(*it, 0, playlists_not_active);
}
w->AddSeparator();
w->AddItem("Cancel");
myScreen = this;
w->Window::Clear();
}
void SelectedItemsAdder::Resize()
@@ -161,7 +157,7 @@ void SelectedItemsAdder::EnterPressed()
MPD::SongList list;
if ((w != itsPlaylistSelector || pos != 0) && pos != w->Size()-1)
myOldScreen->GetSelectedSongs(list);
list = dynamic_cast<HasSongs &>(*myOldScreen).getSelectedSongs();
if (w == itsPlaylistSelector)
{

View File

@@ -35,12 +35,8 @@ void ServerInfo::Init()
SetDimensions();
w = new Scrollpad((COLS-itsWidth)/2, (MainHeight-itsHeight)/2+MainStartY, itsWidth, itsHeight, "MPD server info", Config.main_color, Config.window_border);
Mpd.GetURLHandlers([this](std::string &&handler) {
itsURLHandlers.push_back(handler);
});
Mpd.GetTagTypes([this](std::string &&tag_type) {
itsTagTypes.push_back(tag_type);
});
itsURLHandlers = Mpd.GetURLHandlers();
itsTagTypes = Mpd.GetTagTypes();
isInitialized = 1;
}
@@ -119,11 +115,11 @@ void ServerInfo::Update()
*w << fmtBold << U("Last DB update: ") << fmtBoldEnd << Timestamp(Mpd.DBUpdateTime()) << '\n';
*w << '\n';
*w << fmtBold << U("URL Handlers:") << fmtBoldEnd;
for (MPD::TagList::const_iterator it = itsURLHandlers.begin(); it != itsURLHandlers.end(); ++it)
for (auto it = itsURLHandlers.begin(); it != itsURLHandlers.end(); ++it)
*w << (it != itsURLHandlers.begin() ? U(", ") : U(" ")) << *it;
*w << U("\n\n");
*w << fmtBold << U("Tag Types:") << fmtBoldEnd;
for (MPD::TagList::const_iterator it = itsTagTypes.begin(); it != itsTagTypes.end(); ++it)
for (auto it = itsTagTypes.begin(); it != itsTagTypes.end(); ++it)
*w << (it != itsTagTypes.begin() ? U(", ") : U(" ")) << *it;
w->Flush();

View File

@@ -49,8 +49,8 @@ class ServerInfo : public Screen<Scrollpad>
private:
void SetDimensions();
MPD::TagList itsURLHandlers;
MPD::TagList itsTagTypes;
MPD::StringList itsURLHandlers;
MPD::StringList itsTagTypes;
size_t itsWidth;
size_t itsHeight;

View File

@@ -30,9 +30,9 @@
namespace {//
unsigned calc_hash(const char* s, unsigned seed = 0)
size_t calc_hash(const char* s, unsigned seed = 0)
{
unsigned hash = seed;
size_t hash = seed;
while (*s)
hash = hash * 101 + *s++;
return hash;

View File

@@ -82,7 +82,7 @@ struct Song
const std::string &escape_chars) const;
std::shared_ptr<mpd_song> m_song;
unsigned m_hash;
size_t m_hash;
};
}

View File

@@ -78,8 +78,10 @@ void SongInfo::SwitchTo()
if (myLockedScreen)
UpdateInactiveScreen(this);
MPD::Song *s = myScreen->CurrentSong();
auto hs = dynamic_cast<HasSongs *>(myScreen);
if (!hs)
return;
auto s = hs->currentSong();
if (!s)
return;

View File

@@ -236,19 +236,21 @@ void NcmpcppStatusChanged(MPD::Connection *, MPD::StatusChanges changed, void *)
if (playlist_length < myPlaylist->Items->Size())
myPlaylist->Items->ResizeList(playlist_length);
Mpd.GetPlaylistChanges(Mpd.GetOldPlaylistID(), [](MPD::Song &&s) {
int pos = s.getPosition();
if (pos < int(myPlaylist->Items->Size()))
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())
{
// if song's already in playlist, replace it with a new one
myPlaylist->Items->at(pos).value() = s;
myPlaylist->Items->at(pos).value() = *s;
}
else
{
// otherwise just add it to playlist
myPlaylist->Items->AddItem(s);
myPlaylist->Items->AddItem(*s);
}
});
}
if (is_filtered)
{

View File

@@ -259,32 +259,28 @@ void TagEditor::Update()
{
LeftColumn->Window::Clear();
Tags->Clear();
MPD::TagList list;
if (Config.albums_in_tag_editor)
{
*Albums << XY(0, 0) << "Fetching albums...";
Albums->Window::Refresh();
Mpd.BlockIdle(1); // for the same reason as in media library
Mpd.GetList(list, MPD_TAG_ALBUM);
for (MPD::TagList::const_iterator it = list.begin(); it != list.end(); ++it)
Mpd.BlockIdle(true); // for the same reason as in media library
auto albums = Mpd.GetList(MPD_TAG_ALBUM);
for (auto album = albums.begin(); album != albums.end(); ++album)
{
MPD::SongList l;
Mpd.StartSearch(1);
Mpd.AddSearch(MPD_TAG_ALBUM, *it);
Mpd.CommitSearchSongs([&l](MPD::Song &&s) {
l.push_back(s);
});
if (!l.empty())
Albums->AddItem(std::make_pair(l[0].toString(Config.tag_editor_album_format), *it));
Mpd.AddSearch(MPD_TAG_ALBUM, *album);
auto songs = Mpd.CommitSearchSongs();
if (!songs.empty())
Albums->AddItem(std::make_pair(songs[0].toString(Config.tag_editor_album_format), *album));
}
Mpd.BlockIdle(0);
Mpd.BlockIdle(false);
std::sort(Albums->BeginV(), Albums->EndV(), CaseInsensitiveSorting());
}
else
{
int highlightme = -1;
Mpd.GetDirectories(itsBrowsedDir, list);
sort(list.begin(), list.end(), CaseInsensitiveSorting());
auto dirs = Mpd.GetDirectories(itsBrowsedDir);
std::sort(dirs.begin(), dirs.end(), CaseInsensitiveSorting());
if (itsBrowsedDir != "/")
{
size_t slash = itsBrowsedDir.rfind("/");
@@ -292,16 +288,13 @@ void TagEditor::Update()
Dirs->AddItem(make_pair("..", parent));
}
else
{
Dirs->AddItem(std::make_pair(".", "/"));
}
for (MPD::TagList::const_iterator it = list.begin(); it != list.end(); ++it)
for (auto dir = dirs.begin(); dir != dirs.end(); ++dir)
{
size_t slash = it->rfind("/");
std::string to_display = slash != std::string::npos ? it->substr(slash+1) : *it;
utf_to_locale(to_display);
Dirs->AddItem(make_pair(to_display, *it));
if (*it == itsHighlightedDir)
size_t slash = dir->rfind("/");
std::string to_display = slash != std::string::npos ? dir->substr(slash+1) : *dir;
Dirs->AddItem(make_pair(to_display, *dir));
if (*dir == itsHighlightedDir)
highlightme = Dirs->Size()-1;
}
if (highlightme != -1)
@@ -314,29 +307,25 @@ void TagEditor::Update()
if (Tags->ReallyEmpty())
{
Tags->Reset();
MPD::SongList list;
if (Config.albums_in_tag_editor)
{
if (!Albums->Empty())
{
Mpd.StartSearch(1);
Mpd.AddSearch(MPD_TAG_ALBUM, Albums->Current().value().second);
Mpd.CommitSearchSongs([&list](MPD::Song &&s) {
list.push_back(s);
});
std::sort(list.begin(), list.end(), CaseInsensitiveSorting());
for (auto it = list.begin(); it != list.end(); ++it)
Tags->AddItem(*it);
auto albums = Mpd.CommitSearchSongs();
std::sort(albums.begin(), albums.end(), CaseInsensitiveSorting());
for (auto album = albums.begin(); album != albums.end(); ++album)
Tags->AddItem(*album);
}
}
else
{
Mpd.GetSongs(Dirs->Current().value().second, list);
std::sort(list.begin(), list.end(), CaseInsensitiveSorting());
for (auto it = list.begin(); it != list.end(); ++it)
Tags->AddItem(*it);
auto songs = Mpd.GetSongs(Dirs->Current().value().second);
std::sort(songs.begin(), songs.end(), CaseInsensitiveSorting());
for (auto s = songs.begin(); s != songs.end(); ++s)
Tags->AddItem(*s);
}
Tags->Window::Clear();
Tags->Refresh();
}
@@ -357,9 +346,8 @@ void TagEditor::EnterPressed()
if (w == Dirs)
{
MPD::TagList test;
Mpd.GetDirectories(LeftColumn->Current().value().second, test);
if (!test.empty())
auto dirs = Mpd.GetDirectories(LeftColumn->Current().value().second);
if (!dirs.empty())
{
itsHighlightedDir = itsBrowsedDir;
itsBrowsedDir = LeftColumn->Current().value().second;
@@ -659,7 +647,7 @@ void TagEditor::EnterPressed()
w->Refresh();
w = LeftColumn;
LeftColumn->HighlightColor(Config.active_column_color);
Mpd.UpdateDirectory(getSharedDirectory(Tags));
Mpd.UpdateDirectory(getSharedDirectory(Tags->BeginV(), Tags->EndV()));
}
else
Tags->Clear();
@@ -777,23 +765,6 @@ void TagEditor::MouseButtonPressed(MEVENT me)
}
}
MPD::Song *TagEditor::CurrentSong()
{
return w == Tags && !Tags->Empty() ? &Tags->Current().value() : 0;
}
void TagEditor::GetSelectedSongs(MPD::SongList &v)
{
if (w != Tags || Tags->Empty())
return;
std::vector<size_t> selected;
Tags->GetSelected(selected);
if (selected.empty())
selected.push_back(Tags->Choice());
for (auto it = selected.begin(); it != selected.end(); ++it)
v.push_back(static_cast<MPD::Song>((*Tags)[*it].value()));
}
/***********************************************************************/
std::string TagEditor::currentFilter()
@@ -877,6 +848,56 @@ void TagEditor::prevFound(bool wrap)
/***********************************************************************/
MPD::Song *TagEditor::getSong(size_t pos)
{
MPD::Song *ptr = 0;
if (w == Tags)
ptr = &(*Tags)[pos].value();
return ptr;
}
MPD::Song *TagEditor::currentSong()
{
if (w == Tags && !Tags->Empty())
return getSong(Tags->Choice());
else
return 0;
}
bool TagEditor::allowsSelection()
{
return w == Tags;
}
void TagEditor::removeSelection()
{
if (w == Tags)
removeSelectionHelper(Tags->Begin(), Tags->End());
}
void TagEditor::reverseSelection()
{
if (w == Tags)
reverseSelectionHelper(Tags->Begin(), Tags->End());
}
MPD::SongList TagEditor::getSelectedSongs()
{
MPD::SongList result;
if (w == Tags)
{
for (auto it = Tags->Begin(); it != Tags->End(); ++it)
if (it->isSelected())
result.push_back(it->value());
// if no song was selected, add current one
if (result.empty() && !Tags->Empty())
result.push_back(Tags->Current().value());
}
return result;
}
/***********************************************************************/
List *TagEditor::GetList()
{
if (w == LeftColumn)

View File

@@ -37,7 +37,7 @@
#include "regex_filter.h"
#include "screen.h"
class TagEditor : public Screen<Window>, public Filterable, public Searchable
class TagEditor : public Screen<Window>, public Filterable, public HasSongs, public Searchable
{
public:
TagEditor() : FParser(0), FParserHelper(0), FParserLegend(0), FParserPreview(0), itsBrowsedDir("/") { }
@@ -55,13 +55,6 @@ class TagEditor : public Screen<Window>, public Filterable, public Searchable
virtual void MouseButtonPressed(MEVENT);
virtual bool isTabbable() { return true; }
virtual MPD::Song *CurrentSong();
virtual MPD::Song *GetSong(size_t pos) { return w == Tags ? &Tags->at(pos).value() : 0; }
virtual bool allowsSelection() { return w == Tags; }
virtual void ReverseSelection() { Tags->ReverseSelection(); }
virtual void GetSelectedSongs(MPD::SongList &);
/// Filterable implementation
virtual std::string currentFilter();
virtual void applyFilter(const std::string &filter);
@@ -71,6 +64,15 @@ class TagEditor : public Screen<Window>, public Filterable, public Searchable
virtual void nextFound(bool wrap);
virtual void prevFound(bool wrap);
/// HasSongs implementation
virtual MPD::Song *getSong(size_t pos);
virtual MPD::Song *currentSong();
virtual bool allowsSelection();
virtual void reverseSelection();
virtual void removeSelection();
virtual MPD::SongList getSelectedSongs();
virtual List *GetList();
virtual bool isMergable() { return true; }

View File

@@ -237,11 +237,10 @@ void Visualizer::FindOutputID()
if (!Config.visualizer_output_name.empty())
{
size_t i = 0;
Mpd.GetOutputs([this, &i](MPD::Output &&o) {
if (o.name() == Config.visualizer_output_name)
auto outputs = Mpd.GetOutputs();
for (auto o = outputs.begin(); o != outputs.end(); ++o, ++i)
if (o->name() == Config.visualizer_output_name)
itsOutputID = i;
++i;
});
if (itsOutputID == -1)
ShowMessage("There is no output named \"%s\"", Config.visualizer_output_name.c_str());
}