replace ProxySongList with NC::List and SongList

This commit is contained in:
Andrzej Rybczak
2015-05-10 12:14:56 +02:00
parent a7dab01eff
commit a8e2ec5ed0
32 changed files with 951 additions and 766 deletions

View File

@@ -40,6 +40,7 @@ ncmpcpp_SOURCES = \
settings.cpp \ settings.cpp \
song.cpp \ song.cpp \
song_info.cpp \ song_info.cpp \
song_list.cpp \
sort_playlist.cpp \ sort_playlist.cpp \
status.cpp \ status.cpp \
statusbar.cpp \ statusbar.cpp \
@@ -56,6 +57,7 @@ INCLUDES= $(all_includes)
# the library search path. # the library search path.
ncmpcpp_LDFLAGS = $(all_libraries) ncmpcpp_LDFLAGS = $(all_libraries)
noinst_HEADERS = \ noinst_HEADERS = \
helpers/song_iterator_maker.h \
utility/comparators.h \ utility/comparators.h \
utility/conversion.h \ utility/conversion.h \
utility/functional.h \ utility/functional.h \
@@ -90,7 +92,6 @@ noinst_HEADERS = \
mutable_song.h \ mutable_song.h \
outputs.h \ outputs.h \
playlist_editor.h \ playlist_editor.h \
proxy_song_list.h \
regex_filter.h \ regex_filter.h \
runnable_item.h \ runnable_item.h \
screen.h \ screen.h \
@@ -103,6 +104,7 @@ noinst_HEADERS = \
settings.h \ settings.h \
song.h \ song.h \
song_info.h \ song_info.h \
song_list.h \
sort_playlist.h \ sort_playlist.h \
status.h \ status.h \
statusbar.h \ statusbar.h \

View File

@@ -81,6 +81,10 @@ boost::array<
void populateActions(); void populateActions();
bool scrollTagCanBeRun(NC::List *&list, SongList *&songs);
void scrollTagUpRun(NC::List *list, SongList *songs, MPD::Song::GetFunction get);
void scrollTagDownRun(NC::List *list, SongList *songs, MPD::Song::GetFunction get);
void seek(); void seek();
void findItem(const SearchDirection direction); void findItem(const SearchDirection direction);
void listsChangeFinisher(); void listsChangeFinisher();
@@ -358,102 +362,42 @@ void ScrollDown::run()
bool ScrollUpArtist::canBeRun() bool ScrollUpArtist::canBeRun()
{ {
return proxySongList(myScreen); return scrollTagCanBeRun(m_list, m_songs);
} }
void ScrollUpArtist::run() void ScrollUpArtist::run()
{ {
auto pl = proxySongList(myScreen); scrollTagUpRun(m_list, m_songs, &MPD::Song::getArtist);
assert(pl);
if (pl.empty())
return;
size_t pos = pl.choice();
if (MPD::Song *s = pl.getSong(pos))
{
std::string artist = s->getArtist();
while (pos > 0)
{
s = pl.getSong(--pos);
if (!s || s->getArtist() != artist)
break;
}
pl.highlight(pos);
}
} }
bool ScrollUpAlbum::canBeRun() bool ScrollUpAlbum::canBeRun()
{ {
return proxySongList(myScreen); return scrollTagCanBeRun(m_list, m_songs);
} }
void ScrollUpAlbum::run() void ScrollUpAlbum::run()
{ {
auto pl = proxySongList(myScreen); scrollTagUpRun(m_list, m_songs, &MPD::Song::getAlbum);
assert(pl);
if (pl.empty())
return;
size_t pos = pl.choice();
if (MPD::Song *s = pl.getSong(pos))
{
std::string album = s->getAlbum();
while (pos > 0)
{
s = pl.getSong(--pos);
if (!s || s->getAlbum() != album)
break;
}
pl.highlight(pos);
}
} }
bool ScrollDownArtist::canBeRun() bool ScrollDownArtist::canBeRun()
{ {
return proxySongList(myScreen); return scrollTagCanBeRun(m_list, m_songs);
} }
void ScrollDownArtist::run() void ScrollDownArtist::run()
{ {
auto pl = proxySongList(myScreen); scrollTagDownRun(m_list, m_songs, &MPD::Song::getArtist);
assert(pl);
if (pl.empty())
return;
size_t pos = pl.choice();
if (MPD::Song *s = pl.getSong(pos))
{
std::string artist = s->getArtist();
while (pos < pl.size() - 1)
{
s = pl.getSong(++pos);
if (!s || s->getArtist() != artist)
break;
}
pl.highlight(pos);
}
} }
bool ScrollDownAlbum::canBeRun() bool ScrollDownAlbum::canBeRun()
{ {
return proxySongList(myScreen); return scrollTagCanBeRun(m_list, m_songs);
} }
void ScrollDownAlbum::run() void ScrollDownAlbum::run()
{ {
auto pl = proxySongList(myScreen); scrollTagDownRun(m_list, m_songs, &MPD::Song::getAlbum);
assert(pl);
if (pl.empty())
return;
size_t pos = pl.choice();
if (MPD::Song *s = pl.getSong(pos))
{
std::string album = s->getAlbum();
while (pos < pl.size() - 1)
{
s = pl.getSong(++pos);
if (!s || s->getAlbum() != album)
break;
}
pl.highlight(pos);
}
} }
void PageUp::run() void PageUp::run()
@@ -543,19 +487,6 @@ void PressSpace::run()
myScreen->spacePressed(); myScreen->spacePressed();
} }
bool SelectItem::canBeRun()
{
hs = hasSongs(myScreen);
return hs != nullptr && hs->allowsSelection();
}
void SelectItem::run()
{
hs->selectCurrent();
myScreen->scroll(NC::Scroll::Down);
listsChangeFinisher();
}
bool PreviousColumn::canBeRun() bool PreviousColumn::canBeRun()
{ {
auto hc = hasColumns(myScreen); auto hc = hasColumns(myScreen);
@@ -1011,7 +942,7 @@ void ToggleDisplayMode::run()
case DisplayMode::Classic: case DisplayMode::Classic:
Config.playlist_display_mode = DisplayMode::Columns; Config.playlist_display_mode = DisplayMode::Columns;
myPlaylist->main().setItemDisplayer(std::bind( myPlaylist->main().setItemDisplayer(std::bind(
Display::SongsInColumns, ph::_1, myPlaylist->proxySongList() Display::SongsInColumns, ph::_1, std::cref(myPlaylist->main())
)); ));
if (Config.titles_visibility) if (Config.titles_visibility)
myPlaylist->main().setTitle(Display::Columns(myPlaylist->main().getWidth())); myPlaylist->main().setTitle(Display::Columns(myPlaylist->main().getWidth()));
@@ -1021,7 +952,7 @@ void ToggleDisplayMode::run()
case DisplayMode::Columns: case DisplayMode::Columns:
Config.playlist_display_mode = DisplayMode::Classic; Config.playlist_display_mode = DisplayMode::Classic;
myPlaylist->main().setItemDisplayer(std::bind( myPlaylist->main().setItemDisplayer(std::bind(
Display::Songs, ph::_1, myPlaylist->proxySongList(), std::cref(Config.song_list_format) Display::Songs, ph::_1, std::cref(myPlaylist->main()), std::cref(Config.song_list_format)
)); ));
myPlaylist->main().setTitle(""); myPlaylist->main().setTitle("");
} }
@@ -1072,13 +1003,13 @@ void ToggleDisplayMode::run()
case DisplayMode::Classic: case DisplayMode::Classic:
Config.playlist_editor_display_mode = DisplayMode::Columns; Config.playlist_editor_display_mode = DisplayMode::Columns;
myPlaylistEditor->Content.setItemDisplayer(std::bind( myPlaylistEditor->Content.setItemDisplayer(std::bind(
Display::SongsInColumns, ph::_1, myPlaylistEditor->contentProxyList() Display::SongsInColumns, ph::_1, std::cref(myPlaylistEditor->Content)
)); ));
break; break;
case DisplayMode::Columns: case DisplayMode::Columns:
Config.playlist_editor_display_mode = DisplayMode::Classic; Config.playlist_editor_display_mode = DisplayMode::Classic;
myPlaylistEditor->Content.setItemDisplayer(std::bind( myPlaylistEditor->Content.setItemDisplayer(std::bind(
Display::Songs, ph::_1, myPlaylistEditor->contentProxyList(), std::cref(Config.song_list_format) Display::Songs, ph::_1, std::cref(myPlaylistEditor->Content), std::cref(Config.song_list_format)
)); ));
break; break;
} }
@@ -1285,8 +1216,8 @@ void SetVolume::run()
bool EditSong::canBeRun() bool EditSong::canBeRun()
{ {
# ifdef HAVE_TAGLIB_H # ifdef HAVE_TAGLIB_H
return currentSong(myScreen) m_song = currentSong(myScreen);
&& isMPDMusicDirSet(); return m_song != nullptr && isMPDMusicDirSet();
# else # else
return false; return false;
# endif // HAVE_TAGLIB_H # endif // HAVE_TAGLIB_H
@@ -1295,8 +1226,7 @@ bool EditSong::canBeRun()
void EditSong::run() void EditSong::run()
{ {
# ifdef HAVE_TAGLIB_H # ifdef HAVE_TAGLIB_H
auto s = currentSong(myScreen); myTinyTagEditor->SetEdited(*m_song);
myTinyTagEditor->SetEdited(*s);
myTinyTagEditor->switchTo(); myTinyTagEditor->switchTo();
# endif // HAVE_TAGLIB_H # endif // HAVE_TAGLIB_H
} }
@@ -1530,24 +1460,24 @@ void EditLyrics::run()
bool JumpToBrowser::canBeRun() bool JumpToBrowser::canBeRun()
{ {
return currentSong(myScreen); m_song = currentSong(myScreen);
return m_song != nullptr;
} }
void JumpToBrowser::run() void JumpToBrowser::run()
{ {
auto s = currentSong(myScreen); myBrowser->locateSong(*m_song);
myBrowser->locateSong(*s);
} }
bool JumpToMediaLibrary::canBeRun() bool JumpToMediaLibrary::canBeRun()
{ {
return currentSong(myScreen); m_song = currentSong(myScreen);
return m_song != nullptr;
} }
void JumpToMediaLibrary::run() void JumpToMediaLibrary::run()
{ {
auto s = currentSong(myScreen); myLibrary->LocateSong(*m_song);
myLibrary->LocateSong(*s);
} }
bool JumpToPlaylistEditor::canBeRun() bool JumpToPlaylistEditor::canBeRun()
@@ -1598,8 +1528,8 @@ void ToggleScreenLock::run()
bool JumpToTagEditor::canBeRun() bool JumpToTagEditor::canBeRun()
{ {
# ifdef HAVE_TAGLIB_H # ifdef HAVE_TAGLIB_H
return currentSong(myScreen) m_song = currentSong(myScreen);
&& isMPDMusicDirSet(); return m_song != nullptr && isMPDMusicDirSet();
# else # else
return false; return false;
# endif // HAVE_TAGLIB_H # endif // HAVE_TAGLIB_H
@@ -1608,8 +1538,7 @@ bool JumpToTagEditor::canBeRun()
void JumpToTagEditor::run() void JumpToTagEditor::run()
{ {
# ifdef HAVE_TAGLIB_H # ifdef HAVE_TAGLIB_H
auto s = currentSong(myScreen); myTagEditor->LocateSong(*m_song);
myTagEditor->LocateSong(*s);
# endif // HAVE_TAGLIB_H # endif // HAVE_TAGLIB_H
} }
@@ -1656,71 +1585,87 @@ void JumpToPositionInSong::run()
Statusbar::print("Invalid format ([m]:[ss], [s]s, [%]%, [%] accepted)"); Statusbar::print("Invalid format ([m]:[ss], [s]s, [%]%, [%] accepted)");
} }
bool SelectItem::canBeRun()
{
m_list = dynamic_cast<NC::List *>(myScreen->activeWindow());
return m_list != nullptr
&& !m_list->empty()
&& m_list->currentP()->isSelectable();
}
void SelectItem::run()
{
auto current = m_list->currentP();
current->setSelected(!current->isSelected());
myScreen->scroll(NC::Scroll::Down);
listsChangeFinisher();
}
bool ReverseSelection::canBeRun() bool ReverseSelection::canBeRun()
{ {
auto w = hasSongs(myScreen); m_list = dynamic_cast<NC::List *>(myScreen->activeWindow());
return w && w->allowsSelection(); return m_list != nullptr;
} }
void ReverseSelection::run() void ReverseSelection::run()
{ {
auto w = hasSongs(myScreen); for (auto &p : *m_list)
w->reverseSelection(); p.setSelected(!p.isSelected());
Statusbar::print("Selection reversed"); Statusbar::print("Selection reversed");
} }
bool RemoveSelection::canBeRun() bool RemoveSelection::canBeRun()
{ {
return proxySongList(myScreen); m_list = dynamic_cast<NC::List *>(myScreen->activeWindow());
return m_list != nullptr;
} }
void RemoveSelection::run() void RemoveSelection::run()
{ {
auto pl = proxySongList(myScreen); for (auto &p : *m_list)
for (size_t i = 0; i < pl.size(); ++i) p.setSelected(false);
pl.setSelected(i, false);
Statusbar::print("Selection removed"); Statusbar::print("Selection removed");
} }
bool SelectAlbum::canBeRun() bool SelectAlbum::canBeRun()
{ {
auto w = hasSongs(myScreen); auto *w = myScreen->activeWindow();
return w && w->allowsSelection() && w->proxySongList(); if (m_list != static_cast<void *>(w))
m_list = dynamic_cast<NC::List *>(w);
if (m_songs != static_cast<void *>(w))
m_songs = dynamic_cast<SongList *>(w);
return m_list != nullptr && !m_list->empty()
&& m_songs != nullptr;
} }
void SelectAlbum::run() void SelectAlbum::run()
{ {
auto pl = proxySongList(myScreen); const auto front = m_songs->beginS(), current = m_songs->currentS(), end = m_songs->endS();
assert(pl); auto *s = current->get<Bit::Song>();
if (pl.empty()) if (s == nullptr)
return; return;
size_t pos = pl.choice(); auto get = &MPD::Song::getAlbum;
if (MPD::Song *s = pl.getSong(pos)) const std::string tag = s->getTags(get);
// go up
for (auto it = current; it != front;)
{ {
std::string album = s->getAlbum(); --it;
// select song under cursor s = it->get<Bit::Song>();
pl.setSelected(pos, true); if (s == nullptr || s->getTags(get) != tag)
// go up break;
while (pos > 0) it->get<Bit::Properties>().setSelected(true);
{
s = pl.getSong(--pos);
if (!s || s->getAlbum() != album)
break;
else
pl.setSelected(pos, true);
}
// go down
pos = pl.choice();
while (pos < pl.size() - 1)
{
s = pl.getSong(++pos);
if (!s || s->getAlbum() != album)
break;
else
pl.setSelected(pos, true);
}
Statusbar::print("Album around cursor position selected");
} }
// go down
for (auto it = current;;)
{
it->get<Bit::Properties>().setSelected(true);
if (++it == end)
break;
s = it->get<Bit::Song>();
if (s == nullptr || s->getTags(get) != tag)
break;
}
Statusbar::print("Album around cursor position selected");
} }
bool AddSelectedItems::canBeRun() bool AddSelectedItems::canBeRun()
@@ -2600,6 +2545,53 @@ void populateActions()
insert_action(new Actions::ShowServerInfo()); insert_action(new Actions::ShowServerInfo());
} }
bool scrollTagCanBeRun(NC::List *&list, SongList *&songs)
{
auto w = myScreen->activeWindow();
if (list != static_cast<void *>(w))
list = dynamic_cast<NC::List *>(w);
if (songs != static_cast<void *>(w))
songs = dynamic_cast<SongList *>(w);
return list != nullptr && !list->empty()
&& songs != nullptr;
}
void scrollTagUpRun(NC::List *list, SongList *songs, MPD::Song::GetFunction get)
{
const auto front = songs->beginS();
auto it = songs->currentS();
if (auto *s = it->get<Bit::Song>())
{
const std::string tag = s->getTags(get);
while (it != front)
{
--it;
s = it->get<Bit::Song>();
if (s == nullptr || s->getTags(get) != tag)
break;
}
list->highlight(it-front);
}
}
void scrollTagDownRun(NC::List *list, SongList *songs, MPD::Song::GetFunction get)
{
const auto front = songs->beginS(), back = --songs->endS();
auto it = songs->currentS();
if (auto *s = it->get<Bit::Song>())
{
const std::string tag = s->getTags(get);
while (it != back)
{
++it;
s = it->get<Bit::Song>();
if (s == nullptr || s->getTags(get) != tag)
break;
}
list->highlight(it-front);
}
}
void seek() void seek()
{ {
using Global::wHeader; using Global::wHeader;

View File

@@ -27,6 +27,8 @@
#include "interfaces.h" #include "interfaces.h"
#include "window.h" #include "window.h"
struct SongList;
namespace Actions { namespace Actions {
enum class Type enum class Type
@@ -34,7 +36,7 @@ enum class Type
MacroUtility = 0, MacroUtility = 0,
Dummy, MouseEvent, ScrollUp, ScrollDown, ScrollUpArtist, ScrollUpAlbum, Dummy, MouseEvent, ScrollUp, ScrollDown, ScrollUpArtist, ScrollUpAlbum,
ScrollDownArtist, ScrollDownAlbum, PageUp, PageDown, MoveHome, MoveEnd, ScrollDownArtist, ScrollDownAlbum, PageUp, PageDown, MoveHome, MoveEnd,
ToggleInterface, JumpToParentDirectory, PressEnter, PressSpace, SelectItem, PreviousColumn, ToggleInterface, JumpToParentDirectory, PressEnter, PressSpace, PreviousColumn,
NextColumn, MasterScreen, SlaveScreen, VolumeUp, VolumeDown, DeletePlaylistItems, NextColumn, MasterScreen, SlaveScreen, VolumeUp, VolumeDown, DeletePlaylistItems,
DeleteStoredPlaylist, DeleteBrowserItems, ReplaySong, Previous, Next, Pause, DeleteStoredPlaylist, DeleteBrowserItems, ReplaySong, Previous, Next, Pause,
Stop, ExecuteCommand, SavePlaylist, MoveSortOrderUp, MoveSortOrderDown, Stop, ExecuteCommand, SavePlaylist, MoveSortOrderUp, MoveSortOrderDown,
@@ -46,7 +48,7 @@ enum class Type
SetCrossfade, SetVolume, EditSong, EditLibraryTag, EditLibraryAlbum, EditDirectoryName, SetCrossfade, SetVolume, EditSong, EditLibraryTag, EditLibraryAlbum, EditDirectoryName,
EditPlaylistName, EditLyrics, JumpToBrowser, JumpToMediaLibrary, EditPlaylistName, EditLyrics, JumpToBrowser, JumpToMediaLibrary,
JumpToPlaylistEditor, ToggleScreenLock, JumpToTagEditor, JumpToPositionInSong, JumpToPlaylistEditor, ToggleScreenLock, JumpToTagEditor, JumpToPositionInSong,
ReverseSelection, RemoveSelection, SelectAlbum, AddSelectedItems, SelectItem, ReverseSelection, RemoveSelection, SelectAlbum, AddSelectedItems,
CropMainPlaylist, CropPlaylist, ClearMainPlaylist, ClearPlaylist, SortPlaylist, CropMainPlaylist, CropPlaylist, ClearMainPlaylist, ClearPlaylist, SortPlaylist,
ReversePlaylist, Find, FindItemForward, FindItemBackward, ReversePlaylist, Find, FindItemForward, FindItemBackward,
NextFoundItem, PreviousFoundItem, ToggleFindMode, ToggleReplayGainMode, NextFoundItem, PreviousFoundItem, ToggleFindMode, ToggleReplayGainMode,
@@ -161,6 +163,10 @@ struct ScrollUpArtist : public BaseAction
protected: protected:
virtual bool canBeRun(); virtual bool canBeRun();
virtual void run(); virtual void run();
private:
NC::List *m_list;
SongList *m_songs;
}; };
struct ScrollUpAlbum : public BaseAction struct ScrollUpAlbum : public BaseAction
@@ -170,6 +176,10 @@ struct ScrollUpAlbum : public BaseAction
protected: protected:
virtual bool canBeRun(); virtual bool canBeRun();
virtual void run(); virtual void run();
private:
NC::List *m_list;
SongList *m_songs;
}; };
struct ScrollDownArtist : public BaseAction struct ScrollDownArtist : public BaseAction
@@ -179,6 +189,10 @@ struct ScrollDownArtist : public BaseAction
protected: protected:
virtual bool canBeRun(); virtual bool canBeRun();
virtual void run(); virtual void run();
private:
NC::List *m_list;
SongList *m_songs;
}; };
struct ScrollDownAlbum : public BaseAction struct ScrollDownAlbum : public BaseAction
@@ -188,6 +202,10 @@ struct ScrollDownAlbum : public BaseAction
protected: protected:
virtual bool canBeRun(); virtual bool canBeRun();
virtual void run(); virtual void run();
private:
NC::List *m_list;
SongList *m_songs;
}; };
struct PageUp : public BaseAction struct PageUp : public BaseAction
@@ -255,18 +273,6 @@ protected:
virtual void run(); virtual void run();
}; };
struct SelectItem : public BaseAction
{
SelectItem() : BaseAction(Type::SelectItem, "select_item") { }
protected:
virtual bool canBeRun();
virtual void run();
private:
HasSongs *hs;
};
struct PreviousColumn : public BaseAction struct PreviousColumn : public BaseAction
{ {
PreviousColumn() : BaseAction(Type::PreviousColumn, "previous_column") { } PreviousColumn() : BaseAction(Type::PreviousColumn, "previous_column") { }
@@ -631,6 +637,9 @@ struct EditSong : public BaseAction
protected: protected:
virtual bool canBeRun(); virtual bool canBeRun();
virtual void run(); virtual void run();
private:
const MPD::Song *m_song;
}; };
struct EditLibraryTag : public BaseAction struct EditLibraryTag : public BaseAction
@@ -685,6 +694,9 @@ struct JumpToBrowser : public BaseAction
protected: protected:
virtual bool canBeRun(); virtual bool canBeRun();
virtual void run(); virtual void run();
private:
const MPD::Song *m_song;
}; };
struct JumpToMediaLibrary : public BaseAction struct JumpToMediaLibrary : public BaseAction
@@ -694,6 +706,9 @@ struct JumpToMediaLibrary : public BaseAction
protected: protected:
virtual bool canBeRun(); virtual bool canBeRun();
virtual void run(); virtual void run();
private:
const MPD::Song *m_song;
}; };
struct JumpToPlaylistEditor : public BaseAction struct JumpToPlaylistEditor : public BaseAction
@@ -720,6 +735,9 @@ struct JumpToTagEditor : public BaseAction
protected: protected:
virtual bool canBeRun(); virtual bool canBeRun();
virtual void run(); virtual void run();
private:
const MPD::Song *m_song;
}; };
struct JumpToPositionInSong : public BaseAction struct JumpToPositionInSong : public BaseAction
@@ -731,6 +749,18 @@ protected:
virtual void run(); virtual void run();
}; };
struct SelectItem : public BaseAction
{
SelectItem() : BaseAction(Type::SelectItem, "select_item") { }
protected:
virtual bool canBeRun();
virtual void run();
private:
NC::List *m_list;
};
struct ReverseSelection : public BaseAction struct ReverseSelection : public BaseAction
{ {
ReverseSelection() : BaseAction(Type::ReverseSelection, "reverse_selection") { } ReverseSelection() : BaseAction(Type::ReverseSelection, "reverse_selection") { }
@@ -738,6 +768,9 @@ struct ReverseSelection : public BaseAction
protected: protected:
virtual bool canBeRun(); virtual bool canBeRun();
virtual void run(); virtual void run();
private:
NC::List *m_list;
}; };
struct RemoveSelection : public BaseAction struct RemoveSelection : public BaseAction
@@ -747,6 +780,9 @@ struct RemoveSelection : public BaseAction
protected: protected:
virtual bool canBeRun(); virtual bool canBeRun();
virtual void run(); virtual void run();
private:
NC::List *m_list;
}; };
struct SelectAlbum : public BaseAction struct SelectAlbum : public BaseAction
@@ -756,6 +792,10 @@ struct SelectAlbum : public BaseAction
protected: protected:
virtual bool canBeRun(); virtual bool canBeRun();
virtual void run(); virtual void run();
private:
NC::List *m_list;
SongList *m_songs;
}; };
struct AddSelectedItems : public BaseAction struct AddSelectedItems : public BaseAction

View File

@@ -38,6 +38,7 @@
#include "tag_editor.h" #include "tag_editor.h"
#include "title.h" #include "title.h"
#include "tags.h" #include "tags.h"
#include "helpers/song_iterator_maker.h"
#include "utility/comparators.h" #include "utility/comparators.h"
#include "utility/string.h" #include "utility/string.h"
#include "configuration.h" #include "configuration.h"
@@ -69,8 +70,63 @@ void clearDirectory(const std::string &directory);
std::string itemToString(const MPD::Item &item); std::string itemToString(const MPD::Item &item);
bool browserEntryMatcher(const Regex::Regex &rx, const MPD::Item &item, bool filter); bool browserEntryMatcher(const Regex::Regex &rx, const MPD::Item &item, bool filter);
template <bool Const>
struct SongExtractor
{
typedef SongExtractor type;
typedef typename NC::Menu<MPD::Item>::Item MenuItem;
typedef typename std::conditional<Const, const MenuItem, MenuItem>::type Item;
typedef typename std::conditional<Const, const MPD::Song, MPD::Song>::type Song;
Song *operator()(Item &item) const
{
Song *ptr = nullptr;
if (item.value().type() == MPD::Item::Type::Song)
ptr = const_cast<Song *>(&item.value().song());
return ptr;
}
};
} }
SongIterator BrowserWindow::currentS()
{
return makeSongIterator_<MPD::Item>(current(), SongExtractor<false>());
}
ConstSongIterator BrowserWindow::currentS() const
{
return makeConstSongIterator_<MPD::Item>(current(), SongExtractor<true>());
}
SongIterator BrowserWindow::beginS()
{
return makeSongIterator_<MPD::Item>(begin(), SongExtractor<false>());
}
ConstSongIterator BrowserWindow::beginS() const
{
return makeConstSongIterator_<MPD::Item>(begin(), SongExtractor<true>());
}
SongIterator BrowserWindow::endS()
{
return makeSongIterator_<MPD::Item>(end(), SongExtractor<false>());
}
ConstSongIterator BrowserWindow::endS() const
{
return makeConstSongIterator_<MPD::Item>(end(), SongExtractor<true>());
}
std::vector<MPD::Song> BrowserWindow::getSelectedSongs()
{
return {}; // TODO
}
/**********************************************************************/
Browser::Browser() Browser::Browser()
: m_update_request(true) : m_update_request(true)
, m_local_browser(false) , m_local_browser(false)
@@ -83,7 +139,7 @@ Browser::Browser()
w.centeredCursor(Config.centered_cursor); w.centeredCursor(Config.centered_cursor);
w.setSelectedPrefix(Config.selected_item_prefix); w.setSelectedPrefix(Config.selected_item_prefix);
w.setSelectedSuffix(Config.selected_item_suffix); w.setSelectedSuffix(Config.selected_item_suffix);
w.setItemDisplayer(std::bind(Display::Items, ph::_1, proxySongList())); w.setItemDisplayer(std::bind(Display::Items, ph::_1, std::cref(w)));
} }
void Browser::resize() void Browser::resize()
@@ -110,7 +166,7 @@ void Browser::resize()
void Browser::switchTo() void Browser::switchTo()
{ {
SwitchTo::execute(this); SwitchTo::execute(this);
markSongsInPlaylist(proxySongList()); markSongsInPlaylist(w);
drawHeader(); drawHeader();
} }
@@ -295,34 +351,6 @@ bool Browser::find(SearchDirection direction, bool wrap, bool skip_current)
/***********************************************************************/ /***********************************************************************/
ProxySongList Browser::proxySongList()
{
return ProxySongList(w, [](NC::Menu<MPD::Item>::Item &item) -> MPD::Song * {
MPD::Song *ptr = 0;
if (item.value().type() == MPD::Item::Type::Song)
ptr = const_cast<MPD::Song *>(&item.value().song());
return ptr;
});
}
bool Browser::allowsSelection()
{
size_t root = inRootDirectory() ? 0 : 1;
return !w.empty() && w.choice() >= root;
}
void Browser::selectCurrent()
{
size_t current = w.choice();
w[current].setSelected(!w[current].isSelected());
}
void Browser::reverseSelection()
{
size_t offset = inRootDirectory() ? 0 : 1;
reverseSelectionHelper(w.begin()+offset, w.end());
}
std::vector<MPD::Song> Browser::getSelectedSongs() std::vector<MPD::Song> Browser::getSelectedSongs()
{ {
std::vector<MPD::Song> songs; std::vector<MPD::Song> songs;
@@ -446,7 +474,7 @@ void Browser::getDirectory(std::string directory)
if (!isRootDirectory(directory)) if (!isRootDirectory(directory))
{ {
// make it so that display function doesn't have to handle special cases // make it so that display function doesn't have to handle special cases
w.addItem(MPD::Directory(directory + "/..")); w.addItem(MPD::Directory(directory + "/.."), NC::List::Properties::None);
} }
for (const auto &item : items) for (const auto &item : items)
@@ -468,8 +496,10 @@ void Browser::getDirectory(std::string directory)
} }
case MPD::Item::Type::Song: case MPD::Item::Type::Song:
{ {
bool is_bold = myPlaylist->checkForSong(item.song()); auto properties = NC::List::Properties::Selectable;
w.addItem(std::move(item), is_bold); if (myPlaylist->checkForSong(item.song()))
properties |= NC::List::Properties::Bold;
w.addItem(std::move(item), properties);
break; break;
} }
} }

View File

@@ -25,12 +25,29 @@
#include "mpdpp.h" #include "mpdpp.h"
#include "regex_filter.h" #include "regex_filter.h"
#include "screen.h" #include "screen.h"
#include "song_list.h"
struct Browser: Screen<NC::Menu<MPD::Item>>, HasSongs, Searchable, Tabbable struct BrowserWindow: NC::Menu<MPD::Item>, SongList
{
BrowserWindow() { }
BrowserWindow(NC::Menu<MPD::Item> &&base)
: NC::Menu<MPD::Item>(std::move(base)) { }
virtual SongIterator currentS() OVERRIDE;
virtual ConstSongIterator currentS() const OVERRIDE;
virtual SongIterator beginS() OVERRIDE;
virtual ConstSongIterator beginS() const OVERRIDE;
virtual SongIterator endS() OVERRIDE;
virtual ConstSongIterator endS() const OVERRIDE;
virtual std::vector<MPD::Song> getSelectedSongs() OVERRIDE;
};
struct Browser: Screen<BrowserWindow>, HasSongs, Searchable, Tabbable
{ {
Browser(); Browser();
// Screen< NC::Menu<MPD::Item> > implementation // Screen<BrowserWindow> implementation
virtual void resize() OVERRIDE; virtual void resize() OVERRIDE;
virtual void switchTo() OVERRIDE; virtual void switchTo() OVERRIDE;
@@ -53,11 +70,6 @@ struct Browser: Screen<NC::Menu<MPD::Item>>, HasSongs, Searchable, Tabbable
virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE; virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE;
// HasSongs implementation // HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE;
virtual bool allowsSelection() OVERRIDE;
virtual void selectCurrent() OVERRIDE;
virtual void reverseSelection() OVERRIDE;
virtual std::vector<MPD::Song> getSelectedSongs() OVERRIDE; virtual std::vector<MPD::Song> getSelectedSongs() OVERRIDE;
// private members // private members

View File

@@ -77,27 +77,30 @@ const wchar_t *toColumnName(char c)
} }
template <typename T> template <typename T>
void setProperties(NC::Menu<T> &menu, const MPD::Song &s, const ProxySongList &pl, bool &separate_albums, void setProperties(NC::Menu<T> &menu, const MPD::Song &s, const SongList &list,
bool &is_now_playing, bool &is_selected, bool &discard_colors) bool &separate_albums, bool &is_now_playing, bool &is_selected, bool &discard_colors)
{ {
size_t drawn_pos = menu.drawn() - menu.begin(); size_t drawn_pos = menu.drawn() - menu.begin();
separate_albums = false; separate_albums = false;
if (Config.playlist_separate_albums) if (Config.playlist_separate_albums)
{ {
size_t next_pos = drawn_pos+1; auto next = list.beginS() + drawn_pos + 1;
auto next = next_pos < pl.size() ? pl.getSong(next_pos) : 0; if (next != list.endS())
if (next && next->getAlbum() != s.getAlbum()) {
separate_albums = true; auto next_s = next->get<Bit::Song>();
if (next_s != nullptr && next_s->getAlbum() != s.getAlbum())
separate_albums = true;
}
} }
if (separate_albums) if (separate_albums)
{ {
menu << NC::Format::Underline; menu << NC::Format::Underline;
mvwhline(menu.raw(), menu.getY(), 0, KEY_SPACE, menu.getWidth()); mvwhline(menu.raw(), menu.getY(), 0, KEY_SPACE, menu.getWidth());
} }
is_selected = menu.drawn()->isSelected(); is_selected = menu.drawn()->isSelected();
discard_colors = Config.discard_colors_if_item_is_selected && is_selected; discard_colors = Config.discard_colors_if_item_is_selected && is_selected;
int song_pos = drawn_pos; int song_pos = drawn_pos;
is_now_playing = Status::State::player() != MPD::psStop && myPlaylist->isActiveWindow(menu) is_now_playing = Status::State::player() != MPD::psStop && myPlaylist->isActiveWindow(menu)
&& song_pos == Status::State::currentSongPosition(); && song_pos == Status::State::currentSongPosition();
@@ -106,11 +109,10 @@ void setProperties(NC::Menu<T> &menu, const MPD::Song &s, const ProxySongList &p
} }
template <typename T> template <typename T>
void showSongs(NC::Menu<T> &menu, const MPD::Song &s, void showSongs(NC::Menu<T> &menu, const MPD::Song &s, const SongList &list, const Format::AST<char> &ast)
const ProxySongList &pl, const Format::AST<char> &ast)
{ {
bool separate_albums, is_now_playing, is_selected, discard_colors; bool separate_albums, is_now_playing, is_selected, discard_colors;
setProperties(menu, s, pl, separate_albums, is_now_playing, is_selected, discard_colors); setProperties(menu, s, list, separate_albums, is_now_playing, is_selected, discard_colors);
const size_t y = menu.getY(); const size_t y = menu.getY();
NC::Buffer right_aligned; NC::Buffer right_aligned;
@@ -134,14 +136,14 @@ void showSongs(NC::Menu<T> &menu, const MPD::Song &s,
} }
template <typename T> template <typename T>
void showSongsInColumns(NC::Menu<T> &menu, const MPD::Song &s, const ProxySongList &pl) void showSongsInColumns(NC::Menu<T> &menu, const MPD::Song &s, const SongList &list)
{ {
if (Config.columns.empty()) if (Config.columns.empty())
return; return;
bool separate_albums, is_now_playing, is_selected, discard_colors; bool separate_albums, is_now_playing, is_selected, discard_colors;
setProperties(menu, s, pl, separate_albums, is_now_playing, is_selected, discard_colors); setProperties(menu, s, list, separate_albums, is_now_playing, is_selected, discard_colors);
int width; int width;
int y = menu.getY(); int y = menu.getY();
int remained_width = menu.getWidth(); int remained_width = menu.getWidth();
@@ -163,7 +165,7 @@ void showSongsInColumns(NC::Menu<T> &menu, const MPD::Song &s, const ProxySongLi
// and next column, so we substract it now and restore later. // and next column, so we substract it now and restore later.
if (it != last) if (it != last)
--width; --width;
if (it == Config.columns.begin() && (is_now_playing || is_selected)) if (it == Config.columns.begin() && (is_now_playing || is_selected))
{ {
// here comes the shitty part. if we applied now playing or selected // here comes the shitty part. if we applied now playing or selected
@@ -187,11 +189,11 @@ void showSongsInColumns(NC::Menu<T> &menu, const MPD::Song &s, const ProxySongLi
width -= offset; width -= offset;
remained_width -= offset; remained_width -= offset;
} }
// if column doesn't fit into screen, discard it and any other after it. // if column doesn't fit into screen, discard it and any other after it.
if (remained_width-width < 0 || width < 0 /* this one may come from (*) */) if (remained_width-width < 0 || width < 0 /* this one may come from (*) */)
break; break;
std::wstring tag; std::wstring tag;
for (size_t i = 0; i < it->type.length(); ++i) for (size_t i = 0; i < it->type.length(); ++i)
{ {
@@ -204,16 +206,16 @@ void showSongsInColumns(NC::Menu<T> &menu, const MPD::Song &s, const ProxySongLi
if (tag.empty() && it->display_empty_tag) if (tag.empty() && it->display_empty_tag)
tag = ToWString(Config.empty_tag); tag = ToWString(Config.empty_tag);
wideCut(tag, width); wideCut(tag, width);
if (!discard_colors && it->color != NC::Color::Default) if (!discard_colors && it->color != NC::Color::Default)
menu << it->color; menu << it->color;
int x_off = 0; int x_off = 0;
// if column uses right alignment, calculate proper offset. // if column uses right alignment, calculate proper offset.
// otherwise just assume offset is 0, ie. we start from the left. // otherwise just assume offset is 0, ie. we start from the left.
if (it->right_alignment) if (it->right_alignment)
x_off = std::max(0, width - int(wideLength(tag))); x_off = std::max(0, width - int(wideLength(tag)));
whline(menu.raw(), KEY_SPACE, width); whline(menu.raw(), KEY_SPACE, width);
menu.goToXY(x + x_off, y); menu.goToXY(x + x_off, y);
menu << tag; menu << tag;
@@ -224,11 +226,11 @@ void showSongsInColumns(NC::Menu<T> &menu, const MPD::Song &s, const ProxySongLi
menu << ' '; menu << ' ';
remained_width -= width+1; remained_width -= width+1;
} }
if (!discard_colors && it->color != NC::Color::Default) if (!discard_colors && it->color != NC::Color::Default)
menu << NC::Color::End; menu << NC::Color::End;
} }
// here comes the shitty part, second chapter. here we apply // here comes the shitty part, second chapter. here we apply
// now playing suffix or/and make room for selected suffix // now playing suffix or/and make room for selected suffix
// (as it will be applied in Menu::Refresh when this function // (as it will be applied in Menu::Refresh when this function
@@ -243,7 +245,7 @@ void showSongsInColumns(NC::Menu<T> &menu, const MPD::Song &s, const ProxySongLi
} }
if (is_selected) if (is_selected)
menu.goToXY(menu.getWidth() - Config.selected_item_suffix_length, y); menu.goToXY(menu.getWidth() - Config.selected_item_suffix_length, y);
if (separate_albums) if (separate_albums)
menu << NC::Format::NoUnderline; menu << NC::Format::NoUnderline;
} }
@@ -320,15 +322,14 @@ std::string Display::Columns(size_t list_width)
return result; return result;
} }
void Display::SongsInColumns(NC::Menu< MPD::Song >& menu, const ProxySongList &pl) void Display::SongsInColumns(NC::Menu<MPD::Song> &menu, const SongList &list)
{ {
showSongsInColumns(menu, menu.drawn()->value(), pl); showSongsInColumns(menu, menu.drawn()->value(), list);
} }
void Display::Songs(NC::Menu< MPD::Song >& menu, void Display::Songs(NC::Menu<MPD::Song> &menu, const SongList &list, const Format::AST<char> &ast)
const ProxySongList &pl, const Format::AST<char> &ast)
{ {
showSongs(menu, menu.drawn()->value(), pl, ast); showSongs(menu, menu.drawn()->value(), list, ast);
} }
#ifdef HAVE_TAGLIB_H #ifdef HAVE_TAGLIB_H
@@ -354,7 +355,7 @@ void Display::Tags(NC::Menu<MPD::MutableSong> &menu)
} }
#endif // HAVE_TAGLIB_H #endif // HAVE_TAGLIB_H
void Display::Items(NC::Menu<MPD::Item> &menu, const ProxySongList &pl) void Display::Items(NC::Menu<MPD::Item> &menu, const SongList &list)
{ {
const MPD::Item &item = menu.drawn()->value(); const MPD::Item &item = menu.drawn()->value();
switch (item.type()) switch (item.type())
@@ -368,10 +369,10 @@ void Display::Items(NC::Menu<MPD::Item> &menu, const ProxySongList &pl)
switch (Config.browser_display_mode) switch (Config.browser_display_mode)
{ {
case DisplayMode::Classic: case DisplayMode::Classic:
showSongs(menu, item.song(), pl, Config.song_list_format); showSongs(menu, item.song(), list, Config.song_list_format);
break; break;
case DisplayMode::Columns: case DisplayMode::Columns:
showSongsInColumns(menu, item.song(), pl); showSongsInColumns(menu, item.song(), list);
break; break;
} }
break; break;
@@ -382,7 +383,7 @@ void Display::Items(NC::Menu<MPD::Item> &menu, const ProxySongList &pl)
} }
} }
void Display::SEItems(NC::Menu<SEItem> &menu, const ProxySongList &pl) void Display::SEItems(NC::Menu<SEItem> &menu, const SongList &list)
{ {
const SEItem &si = menu.drawn()->value(); const SEItem &si = menu.drawn()->value();
if (si.isSong()) if (si.isSong())
@@ -390,10 +391,10 @@ void Display::SEItems(NC::Menu<SEItem> &menu, const ProxySongList &pl)
switch (Config.search_engine_display_mode) switch (Config.search_engine_display_mode)
{ {
case DisplayMode::Classic: case DisplayMode::Classic:
showSongs(menu, si.song(), pl, Config.song_list_format); showSongs(menu, si.song(), list, Config.song_list_format);
break; break;
case DisplayMode::Columns: case DisplayMode::Columns:
showSongsInColumns(menu, si.song(), pl); showSongsInColumns(menu, si.song(), list);
break; break;
} }
} }

View File

@@ -26,20 +26,21 @@
#include "menu.h" #include "menu.h"
#include "mutable_song.h" #include "mutable_song.h"
#include "search_engine.h" #include "search_engine.h"
#include "song_list.h"
namespace Display { namespace Display {
std::string Columns(size_t); std::string Columns(size_t);
void SongsInColumns(NC::Menu<MPD::Song> &menu, const ProxySongList &pl); void SongsInColumns(NC::Menu<MPD::Song> &menu, const SongList &list);
void Songs(NC::Menu<MPD::Song> &menu, const ProxySongList &pl, const Format::AST<char> &ast); void Songs(NC::Menu<MPD::Song> &menu, const SongList &list, const Format::AST<char> &ast);
void Tags(NC::Menu<MPD::MutableSong> &menu); void Tags(NC::Menu<MPD::MutableSong> &menu);
void SEItems(NC::Menu<SEItem> &menu, const ProxySongList &pl); void SEItems(NC::Menu<SEItem> &menu, const SongList &list);
void Items(NC::Menu<MPD::Item> &menu, const ProxySongList &pl); void Items(NC::Menu<MPD::Item> &menu, const SongList &list);
} }

View File

@@ -25,6 +25,69 @@
#include "playlist.h" #include "playlist.h"
#include "statusbar.h" #include "statusbar.h"
const MPD::Song *currentSong(const BaseScreen *screen)
{
const MPD::Song *ptr = nullptr;
const auto *list = dynamic_cast<const SongList *>(screen->activeWindow());
if (list != nullptr)
{
const auto it = list->currentS();
if (it != list->endS())
ptr = it->get<Bit::Song>();
}
return ptr;
}
typedef std::vector<MPD::Song>::const_iterator VectorSongIterator;
bool addSongsToPlaylist(VectorSongIterator first, VectorSongIterator last, bool play, int position)
{
bool result = true;
auto addSongNoError = [&](VectorSongIterator song) -> int {
try
{
return Mpd.AddSong(*song, position);
}
catch (MPD::ServerError &e)
{
Status::handleServerError(e);
result = false;
return -1;
}
};
if (last-first >= 1)
{
int id;
while (true)
{
id = addSongNoError(first);
if (id >= 0)
break;
++first;
if (first == last)
return result;
}
if (position == -1)
{
++first;
for(; first != last; ++first)
addSongNoError(first);
}
else
{
++position;
--last;
for (; first != last; --last)
addSongNoError(last);
}
if (play)
Mpd.PlayID(id);
}
return result;
}
bool addSongToPlaylist(const MPD::Song &s, bool play, int position) bool addSongToPlaylist(const MPD::Song &s, bool play, int position)
{ {
bool result = false; bool result = false;
@@ -81,12 +144,15 @@ std::string Timestamp(time_t t)
return result; return result;
} }
void markSongsInPlaylist(ProxySongList pl) void markSongsInPlaylist(SongList &list)
{ {
size_t list_size = pl.size(); MPD::Song *s;
for (size_t i = 0; i < list_size; ++i) for (auto &p : list)
if (auto s = pl.getSong(i)) {
pl.setBold(i, myPlaylist->checkForSong(*s)); s = p.get<Bit::Song>();
if (s != nullptr)
p.get<Bit::Properties>().setBold(myPlaylist->checkForSong(*s));
}
} }
std::wstring Scroller(const std::wstring &str, size_t &pos, size_t width) std::wstring Scroller(const std::wstring &str, size_t &pos, size_t width)

View File

@@ -25,6 +25,7 @@
#include "mpdpp.h" #include "mpdpp.h"
#include "screen.h" #include "screen.h"
#include "settings.h" #include "settings.h"
#include "song_list.h"
#include "status.h" #include "status.h"
#include "utility/string.h" #include "utility/string.h"
#include "utility/type_conversions.h" #include "utility/type_conversions.h"
@@ -93,24 +94,6 @@ inline HasSongs *hasSongs(BaseScreen *screen)
return dynamic_cast<HasSongs *>(screen); return dynamic_cast<HasSongs *>(screen);
} }
inline ProxySongList proxySongList(BaseScreen *screen)
{
auto pl = ProxySongList();
auto hs = hasSongs(screen);
if (hs)
pl = hs->proxySongList();
return pl;
}
inline MPD::Song *currentSong(BaseScreen *screen)
{
MPD::Song *ptr = 0;
auto pl = proxySongList(screen);
if (pl && !pl.empty())
ptr = pl.currentSong();
return ptr;
}
template <typename Iterator> template <typename Iterator>
bool hasSelected(Iterator first, Iterator last) bool hasSelected(Iterator first, Iterator last)
{ {
@@ -314,12 +297,6 @@ void cropPlaylist(NC::Menu<MPD::Song> &m, F delete_fun)
deleteSelectedSongs(m, delete_fun); deleteSelectedSongs(m, delete_fun);
} }
template <typename ItemT>
std::function<void (ItemT)> vectorMoveInserter(std::vector<ItemT> &v)
{
return [&](ItemT item) { v.push_back(std::move(item)); };
}
template <typename Iterator> std::string getSharedDirectory(Iterator first, Iterator last) template <typename Iterator> std::string getSharedDirectory(Iterator first, Iterator last)
{ {
assert(first != last); assert(first != last);
@@ -384,68 +361,25 @@ template <typename BufferT> void ShowTag(BufferT &buf, const std::string &tag)
buf << tag; buf << tag;
} }
template <typename SongIterator>
bool addSongsToPlaylist(SongIterator first, SongIterator last, bool play, int position)
{
bool result = true;
auto addSongNoError = [&](SongIterator song) -> int {
try
{
return Mpd.AddSong(*song, position);
}
catch (MPD::ServerError &e)
{
Status::handleServerError(e);
result = false;
return -1;
}
};
if (last-first >= 1)
{
int id;
while (true)
{
id = addSongNoError(first);
if (id >= 0)
break;
++first;
if (first == last)
return result;
}
if (position == -1)
{
++first;
for(; first != last; ++first)
addSongNoError(first);
}
else
{
++position;
--last;
for (; first != last; --last)
addSongNoError(last);
}
if (play)
Mpd.PlayID(id);
}
return result;
}
inline const char *withErrors(bool success) inline const char *withErrors(bool success)
{ {
return success ? "" : " " "(with errors)"; return success ? "" : " " "(with errors)";
} }
bool addSongsToPlaylist(std::vector<MPD::Song>::const_iterator first,
std::vector<MPD::Song>::const_iterator last,
bool play, int position);
bool addSongToPlaylist(const MPD::Song &s, bool play, int position = -1); bool addSongToPlaylist(const MPD::Song &s, bool play, int position = -1);
const MPD::Song *currentSong(const BaseScreen *screen);
std::string timeFormat(const char *format, time_t t); std::string timeFormat(const char *format, time_t t);
std::string Timestamp(time_t t); std::string Timestamp(time_t t);
void markSongsInPlaylist(ProxySongList pl); void markSongsInPlaylist(SongList &list);
std::wstring Scroller(const std::wstring &str, size_t &pos, size_t width); std::wstring Scroller(const std::wstring &str, size_t &pos, size_t width);
void writeCyclicBuffer(const NC::WBuffer &buf, NC::Window &w, size_t &start_pos, void writeCyclicBuffer(const NC::WBuffer &buf, NC::Window &w, size_t &start_pos,

View File

@@ -0,0 +1,47 @@
/***************************************************************************
* Copyright (C) 2008-2014 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. *
***************************************************************************/
#ifndef NCMPCPP_HELPERS_SONG_ITERATOR_MAKER_H
#define NCMPCPP_HELPERS_SONG_ITERATOR_MAKER_H
#include <boost/iterator/transform_iterator.hpp>
#include <boost/iterator/zip_iterator.hpp>
#include "menu.h"
#include "song_list.h"
template <typename ItemT, typename TransformT>
SongIterator makeSongIterator_(typename NC::Menu<ItemT>::Iterator it, TransformT &&map)
{
return SongIterator(boost::make_zip_iterator(boost::make_tuple(
typename NC::Menu<ItemT>::PropertiesIterator(it),
boost::make_transform_iterator(it, std::forward<TransformT>(map))
)));
}
template <typename ItemT, typename TransformT>
ConstSongIterator makeConstSongIterator_(typename NC::Menu<ItemT>::ConstIterator it, TransformT &&map)
{
return ConstSongIterator(boost::make_zip_iterator(boost::make_tuple(
typename NC::Menu<ItemT>::ConstPropertiesIterator(it),
boost::make_transform_iterator(it, std::forward<TransformT>(map))
)));
}
#endif // NCMPCPP_HELPERS_SONG_ITERATOR_MAKER_H

View File

@@ -21,12 +21,13 @@
#ifndef NCMPCPP_INTERFACES_H #ifndef NCMPCPP_INTERFACES_H
#define NCMPCPP_INTERFACES_H #define NCMPCPP_INTERFACES_H
#include <boost/range/detail/any_iterator.hpp>
#include <boost/tuple/tuple.hpp>
#include <string> #include <string>
#include "enums.h" #include "enums.h"
#include "gcc.h" #include "gcc.h"
#include "screen.h" #include "screen.h"
#include "song.h" #include "song.h"
#include "proxy_song_list.h"
struct Searchable struct Searchable
{ {
@@ -38,11 +39,6 @@ struct Searchable
struct HasSongs struct HasSongs
{ {
virtual ProxySongList proxySongList() = 0;
virtual bool allowsSelection() = 0;
virtual void reverseSelection() = 0;
virtual void selectCurrent() = 0;
virtual std::vector<MPD::Song> getSelectedSongs() = 0; virtual std::vector<MPD::Song> getSelectedSongs() = 0;
}; };

View File

@@ -34,7 +34,9 @@
#include "media_library.h" #include "media_library.h"
#include "status.h" #include "status.h"
#include "statusbar.h" #include "statusbar.h"
#include "helpers/song_iterator_maker.h"
#include "utility/comparators.h" #include "utility/comparators.h"
#include "utility/functional.h"
#include "utility/type_conversions.h" #include "utility/type_conversions.h"
#include "title.h" #include "title.h"
#include "screen_switcher.h" #include "screen_switcher.h"
@@ -202,7 +204,7 @@ MediaLibrary::MediaLibrary()
Songs.setSelectedPrefix(Config.selected_item_prefix); Songs.setSelectedPrefix(Config.selected_item_prefix);
Songs.setSelectedSuffix(Config.selected_item_suffix); Songs.setSelectedSuffix(Config.selected_item_suffix);
Songs.setItemDisplayer(std::bind( Songs.setItemDisplayer(std::bind(
Display::Songs, ph::_1, songsProxyList(), std::cref(Config.song_library_format) Display::Songs, ph::_1, std::cref(Songs), std::cref(Config.song_library_format)
)); ));
w = &Tags; w = &Tags;
@@ -257,7 +259,7 @@ void MediaLibrary::refresh()
void MediaLibrary::switchTo() void MediaLibrary::switchTo()
{ {
SwitchTo::execute(this); SwitchTo::execute(this);
markSongsInPlaylist(songsProxyList()); markSongsInPlaylist(Songs);
drawHeader(); drawHeader();
refresh(); refresh();
} }
@@ -418,14 +420,19 @@ void MediaLibrary::update()
size_t idx = 0; size_t idx = 0;
for (MPD::SongIterator s = Mpd.CommitSearchSongs(), end; s != end; ++s, ++idx) for (MPD::SongIterator s = Mpd.CommitSearchSongs(), end; s != end; ++s, ++idx)
{ {
bool is_playlist = myPlaylist->checkForSong(*s); bool in_playlist = myPlaylist->checkForSong(*s);
if (idx < Songs.size()) if (idx < Songs.size())
{ {
Songs[idx].value() = std::move(*s); Songs[idx].value() = std::move(*s);
Songs[idx].setBold(is_playlist); Songs[idx].setBold(in_playlist);
} }
else else
Songs.addItem(std::move(*s), is_playlist); {
auto properties = NC::List::Properties::Selectable;
if (in_playlist)
properties |= NC::List::Properties::Bold;
Songs.addItem(std::move(*s), properties);
}
}; };
if (idx < Songs.size()) if (idx < Songs.size())
Songs.resizeList(idx); Songs.resizeList(idx);
@@ -601,56 +608,6 @@ bool MediaLibrary::find(SearchDirection direction, bool wrap, bool skip_current)
/***********************************************************************/ /***********************************************************************/
ProxySongList MediaLibrary::proxySongList()
{
auto ptr = ProxySongList();
if (isActiveWindow(Songs))
ptr = songsProxyList();
return ptr;
}
bool MediaLibrary::allowsSelection()
{
return (isActiveWindow(Tags) && !Tags.empty())
|| (isActiveWindow(Albums) && !Albums.empty() && !Albums.current()->value().isAllTracksEntry())
|| (isActiveWindow(Songs) && !Songs.empty());
}
void MediaLibrary::selectCurrent()
{
if (isActiveWindow(Tags))
{
size_t idx = Tags.choice();
Tags[idx].setSelected(!Tags[idx].isSelected());
}
else if (isActiveWindow(Albums))
{
size_t idx = Albums.choice();
Albums[idx].setSelected(!Albums[idx].isSelected());
}
else if (isActiveWindow(Songs))
{
size_t idx = Songs.choice();
Songs[idx].setSelected(!Songs[idx].isSelected());
}
}
void MediaLibrary::reverseSelection()
{
if (isActiveWindow(Tags))
reverseSelectionHelper(Tags.begin(), Tags.end());
else if (isActiveWindow(Albums))
{
// omit "All tracks"
if (Albums.size() > 1)
reverseSelectionHelper(Albums.begin(), Albums.end()-2);
else
reverseSelectionHelper(Albums.begin(), Albums.end());
}
else if (isActiveWindow(Songs))
reverseSelectionHelper(Songs.begin(), Songs.end());
}
std::vector<MPD::Song> MediaLibrary::getSelectedSongs() std::vector<MPD::Song> MediaLibrary::getSelectedSongs()
{ {
std::vector<MPD::Song> result; std::vector<MPD::Song> result;
@@ -717,14 +674,7 @@ std::vector<MPD::Song> MediaLibrary::getSelectedSongs()
} }
} }
else if (isActiveWindow(Songs)) else if (isActiveWindow(Songs))
{ Songs.getSelectedSongs();
for (const auto &s : Songs)
if (s.isSelected())
result.push_back(s.value());
// if no item is selected, add current one
if (result.empty() && !Songs.empty())
result.push_back(Songs.current()->value());
}
return result; return result;
} }
@@ -837,13 +787,6 @@ int MediaLibrary::Columns()
return 3; return 3;
} }
ProxySongList MediaLibrary::songsProxyList()
{
return ProxySongList(Songs, [](NC::Menu<MPD::Song>::Item &item) {
return &item.value();
});
}
void MediaLibrary::toggleSortMode() void MediaLibrary::toggleSortMode()
{ {
Config.media_library_sort_by_mtime = !Config.media_library_sort_by_mtime; Config.media_library_sort_by_mtime = !Config.media_library_sort_by_mtime;

View File

@@ -26,6 +26,7 @@
#include "interfaces.h" #include "interfaces.h"
#include "regex_filter.h" #include "regex_filter.h"
#include "screen.h" #include "screen.h"
#include "song_list.h"
struct MediaLibrary: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tabbable struct MediaLibrary: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tabbable
{ {
@@ -56,11 +57,6 @@ struct MediaLibrary: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tab
virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE; virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE;
// HasSongs implementation // HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE;
virtual bool allowsSelection() OVERRIDE;
virtual void selectCurrent() OVERRIDE;
virtual void reverseSelection() OVERRIDE;
virtual std::vector<MPD::Song> getSelectedSongs() OVERRIDE; virtual std::vector<MPD::Song> getSelectedSongs() OVERRIDE;
// HasColumns implementation // HasColumns implementation
@@ -75,7 +71,6 @@ struct MediaLibrary: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tab
void toggleColumnsMode(); void toggleColumnsMode();
int Columns(); int Columns();
void LocateSong(const MPD::Song &); void LocateSong(const MPD::Song &);
ProxySongList songsProxyList();
void toggleSortMode(); void toggleSortMode();
void requestTagsUpdate() { m_tags_update_request = true; } void requestTagsUpdate() { m_tags_update_request = true; }
@@ -135,7 +130,7 @@ struct MediaLibrary: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tab
NC::Menu<PrimaryTag> Tags; NC::Menu<PrimaryTag> Tags;
NC::Menu<AlbumEntry> Albums; NC::Menu<AlbumEntry> Albums;
NC::Menu<MPD::Song> Songs; SongMenu Songs;
private: private:
void AddToPlaylist(bool); void AddToPlaylist(bool);

View File

@@ -22,6 +22,8 @@
#define NCMPCPP_MENU_H #define NCMPCPP_MENU_H
#include <boost/iterator/indirect_iterator.hpp> #include <boost/iterator/indirect_iterator.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/range/detail/any_iterator.hpp>
#include <cassert> #include <cassert>
#include <functional> #include <functional>
#include <iterator> #include <iterator>
@@ -33,21 +35,150 @@
namespace NC { namespace NC {
struct List
{
struct Properties
{
enum Type {
None = 0,
Bold = (1 << 0),
Selectable = (1 << 1),
Selected = (1 << 2),
Inactive = (1 << 3),
Separator = (1 << 4)
};
Properties(Type properties = Selectable)
: m_properties(properties)
{ }
void setBold(bool is_bold)
{
if (is_bold)
m_properties |= Bold;
else
m_properties &= ~Bold;
}
void setSelectable(bool is_selectable)
{
if (is_selectable)
m_properties |= Selectable;
else
m_properties &= ~(Selectable | Selected);
}
void setSelected(bool is_selected)
{
if (!isSelectable())
return;
if (is_selected)
m_properties |= Selected;
else
m_properties &= ~Selected;
}
void setInactive(bool is_inactive)
{
if (is_inactive)
m_properties |= Inactive;
else
m_properties &= ~Inactive;
}
void setSeparator(bool is_separator)
{
if (is_separator)
m_properties |= Separator;
else
m_properties &= ~Separator;
}
bool isBold() const { return m_properties & Bold; }
bool isSelectable() const { return m_properties & Selectable; }
bool isSelected() const { return m_properties & Selected; }
bool isInactive() const { return m_properties & Inactive; }
bool isSeparator() const { return m_properties & Separator; }
private:
unsigned m_properties;
};
template <typename ValueT>
using PropertiesIterator = boost::range_detail::any_iterator<
ValueT,
boost::random_access_traversal_tag,
ValueT &,
std::ptrdiff_t
>;
typedef PropertiesIterator<Properties> Iterator;
typedef PropertiesIterator<const Properties> ConstIterator;
virtual ~List() { }
virtual bool empty() const = 0;
virtual size_t size() const = 0;
virtual size_t choice() const = 0;
virtual void highlight(size_t pos) = 0;
virtual Iterator currentP() = 0;
virtual ConstIterator currentP() const = 0;
virtual Iterator beginP() = 0;
virtual ConstIterator beginP() const = 0;
virtual Iterator endP() = 0;
virtual ConstIterator endP() const = 0;
};
inline List::Properties::Type operator|(List::Properties::Type lhs, List::Properties::Type rhs)
{
return List::Properties::Type(unsigned(lhs) | unsigned(rhs));
}
inline List::Properties::Type &operator|=(List::Properties::Type &lhs, List::Properties::Type rhs)
{
lhs = lhs | rhs;
return lhs;
}
inline List::Properties::Type operator&(List::Properties::Type lhs, List::Properties::Type rhs)
{
return List::Properties::Type(unsigned(lhs) & unsigned(rhs));
}
inline List::Properties::Type &operator&=(List::Properties::Type &lhs, List::Properties::Type rhs)
{
lhs = lhs & rhs;
return lhs;
}
// for range-based for loop
inline List::Iterator begin(List &list) { return list.beginP(); }
inline List::ConstIterator begin(const List &list) { return list.beginP(); }
inline List::Iterator end(List &list) { return list.endP(); }
inline List::ConstIterator end(const List &list) { return list.endP(); }
/// This template class is generic menu capable of /// This template class is generic menu capable of
/// holding any std::vector compatible values. /// holding any std::vector compatible values.
template <typename ItemT> class Menu : public Window template <typename ItemT> struct Menu : Window, List
{ {
public: struct Item : List::Properties
struct Item
{ {
template <bool Const>
struct PropertiesExtractor
{
typedef PropertiesExtractor type;
typedef typename std::conditional<Const, const Properties, Properties>::type Properties_;
typedef typename std::conditional<Const, const Item, Item>::type Item_;
Properties_ &operator()(Item_ &i) const {
return static_cast<Properties_ &>(i);
}
};
typedef ItemT Type; typedef ItemT Type;
friend class Menu<ItemT>; friend struct Menu<ItemT>;
Item() Item() { }
: m_is_bold(false), m_is_selected(false), m_is_inactive(false), m_is_separator(false) { } Item(ItemT value_, Properties::Type properties)
Item(ItemT value_, bool is_bold, bool is_inactive) : Properties(properties)
: m_value(value_), m_is_bold(is_bold), m_is_selected(false), m_is_inactive(is_inactive), m_is_separator(false) { } , m_value(value_)
{ }
ItemT &value() { return m_value; } ItemT &value() { return m_value; }
const ItemT &value() const { return m_value; } const ItemT &value() const { return m_value; }
@@ -55,29 +186,16 @@ public:
ItemT &operator*() { return m_value; } ItemT &operator*() { return m_value; }
const ItemT &operator*() const { return m_value; } const ItemT &operator*() const { return m_value; }
void setBold(bool is_bold) { m_is_bold = is_bold; }
void setSelected(bool is_selected) { m_is_selected = is_selected; }
void setInactive(bool is_inactive) { m_is_inactive = is_inactive; }
void setSeparator(bool is_separator) { m_is_separator = is_separator; }
bool isBold() const { return m_is_bold; }
bool isSelected() const { return m_is_selected; }
bool isInactive() const { return m_is_inactive; }
bool isSeparator() const { return m_is_separator; }
private: private:
static Item mkSeparator() static Item mkSeparator()
{ {
Item item; Item item;
item.m_is_separator = true; item.setSelectable(false);
item.setSeparator(true);
return item; return item;
} }
ItemT m_value; ItemT m_value;
bool m_is_bold;
bool m_is_selected;
bool m_is_inactive;
bool m_is_separator;
}; };
typedef typename std::vector<Item>::iterator Iterator; typedef typename std::vector<Item>::iterator Iterator;
@@ -98,6 +216,15 @@ public:
typedef std::reverse_iterator<ValueIterator> ReverseValueIterator; typedef std::reverse_iterator<ValueIterator> ReverseValueIterator;
typedef std::reverse_iterator<ConstValueIterator> ConstReverseValueIterator; typedef std::reverse_iterator<ConstValueIterator> ConstReverseValueIterator;
typedef boost::transform_iterator<
typename Item::template PropertiesExtractor<false>,
Iterator
> PropertiesIterator;
typedef boost::transform_iterator<
typename Item::template PropertiesExtractor<true>,
ConstIterator
> ConstPropertiesIterator;
/// Function helper prototype used to display each option on the screen. /// Function helper prototype used to display each option on the screen.
/// If not set by setItemDisplayer(), menu won't display anything. /// If not set by setItemDisplayer(), menu won't display anything.
/// @see setItemDisplayer() /// @see setItemDisplayer()
@@ -120,21 +247,14 @@ public:
/// @param size requested size /// @param size requested size
void resizeList(size_t new_size); void resizeList(size_t new_size);
/// Adds new option to list /// Adds a new option to list
/// @param item object that has to be added void addItem(ItemT item, Properties::Type properties = Properties::Selectable);
/// @param is_bold defines the initial state of bold attribute
/// @param is_inactive defines the initial state of static attribute
void addItem(ItemT item, bool is_bold = false, bool is_inactive = false);
/// Adds separator to list /// Adds separator to list
void addSeparator(); void addSeparator();
/// Inserts new option to list at given position /// Inserts a new option to the list at given position
/// @param pos initial position of inserted item void insertItem(size_t pos, ItemT item, Properties::Type properties = Properties::Selectable);
/// @param item object that has to be inserted
/// @param is_bold defines the initial state of bold attribute
/// @param is_inactive defines the initial state of static attribute
void insertItem(size_t pos, const ItemT &Item, bool is_bold = false, bool is_inactive = false);
/// Inserts separator to list at given position /// Inserts separator to list at given position
/// @param pos initial position of inserted separator /// @param pos initial position of inserted separator
@@ -149,12 +269,19 @@ public:
/// @return true if the position is reachable, false otherwise /// @return true if the position is reachable, false otherwise
bool Goto(size_t y); bool Goto(size_t y);
/// Checks if list is empty
/// @return true if list is empty, false otherwise
virtual bool empty() const OVERRIDE { return m_items.empty(); }
/// @return size of the list
virtual size_t size() const OVERRIDE { return m_items.size(); }
/// @return currently highlighted position
virtual size_t choice() const OVERRIDE;
/// Highlights given position /// Highlights given position
/// @param pos position to be highlighted /// @param pos position to be highlighted
void highlight(size_t pos); virtual void highlight(size_t position) OVERRIDE;
/// @return currently highlighted position
size_t choice() const;
/// Refreshes the menu window /// Refreshes the menu window
/// @see Window::refresh() /// @see Window::refresh()
@@ -201,13 +328,6 @@ public:
/// @param state state of centered cursor /// @param state state of centered cursor
void centeredCursor(bool state) { m_autocenter_cursor = state; } void centeredCursor(bool state) { m_autocenter_cursor = state; }
/// Checks if list is empty
/// @return true if list is empty, false otherwise
bool empty() const { return m_items.empty(); }
/// @return size of the list
size_t size() const { return m_items.size(); }
/// @return currently drawn item. The result is defined only within /// @return currently drawn item. The result is defined only within
/// drawing function that is called by refresh() /// drawing function that is called by refresh()
/// @see refresh() /// @see refresh()
@@ -261,8 +381,26 @@ public:
ReverseValueIterator rendV() { return ReverseValueIterator(beginV()); } ReverseValueIterator rendV() { return ReverseValueIterator(beginV()); }
ConstReverseValueIterator rendV() const { return ConstReverseValueIterator(beginV()); } ConstReverseValueIterator rendV() const { return ConstReverseValueIterator(beginV()); }
private: virtual List::Iterator currentP() OVERRIDE {
return List::Iterator(PropertiesIterator(m_items.begin() + m_highlight));
}
virtual List::ConstIterator currentP() const OVERRIDE {
return List::ConstIterator(ConstPropertiesIterator(m_items.begin() + m_highlight));
}
virtual List::Iterator beginP() OVERRIDE {
return List::Iterator(PropertiesIterator(m_items.begin()));
}
virtual List::ConstIterator beginP() const OVERRIDE {
return List::ConstIterator(ConstPropertiesIterator(m_items.begin()));
}
virtual List::Iterator endP() OVERRIDE {
return List::Iterator(PropertiesIterator(m_items.end()));
}
virtual List::ConstIterator endP() const OVERRIDE {
return List::ConstIterator(ConstPropertiesIterator(m_items.end()));
}
private:
bool isHighlightable(size_t pos) bool isHighlightable(size_t pos)
{ {
return !m_items[pos].isSeparator() return !m_items[pos].isSeparator()

View File

@@ -114,9 +114,9 @@ void Menu<ItemT>::resizeList(size_t new_size)
} }
template <typename ItemT> template <typename ItemT>
void Menu<ItemT>::addItem(ItemT item, bool is_bold, bool is_inactive) void Menu<ItemT>::addItem(ItemT item, Properties::Type properties)
{ {
m_items.push_back(Item(std::move(item), is_bold, is_inactive)); m_items.push_back(Item(std::move(item), properties));
} }
template <typename ItemT> template <typename ItemT>
@@ -126,9 +126,9 @@ void Menu<ItemT>::addSeparator()
} }
template <typename ItemT> template <typename ItemT>
void Menu<ItemT>::insertItem(size_t pos, const ItemT &item, bool is_bold, bool is_inactive) void Menu<ItemT>::insertItem(size_t pos, ItemT item, Properties::Type properties)
{ {
m_items.insert(m_items.begin()+pos, Item(item, is_bold, is_inactive)); m_items.insert(m_items.begin()+pos, Item(std::move(item), properties));
} }
template <typename ItemT> template <typename ItemT>
@@ -181,9 +181,9 @@ void Menu<ItemT>::refresh()
} }
size_t line = 0; size_t line = 0;
const size_t end = m_beginning+m_height; const size_t end_ = m_beginning+m_height;
m_drawn_position = m_beginning; m_drawn_position = m_beginning;
for (; m_drawn_position < end; ++m_drawn_position, ++line) for (; m_drawn_position < end_; ++m_drawn_position, ++line)
{ {
goToXY(0, line); goToXY(0, line);
if (m_drawn_position >= m_items.size()) if (m_drawn_position >= m_items.size())

View File

@@ -102,8 +102,10 @@ void Outputs::FetchList()
w.clear(); w.clear();
for (MPD::OutputIterator out = Mpd.GetOutputs(), end; out != end; ++out) for (MPD::OutputIterator out = Mpd.GetOutputs(), end; out != end; ++out)
{ {
bool enabled = out->enabled(); auto properties = NC::List::Properties::Selectable;
w.addItem(std::move(*out), enabled); if (out->enabled())
properties |= NC::List::Properties::Bold;
w.addItem(std::move(*out), properties);
} }
if (myScreen == this) if (myScreen == this)
w.refresh(); w.refresh();

View File

@@ -31,7 +31,9 @@
#include "song.h" #include "song.h"
#include "status.h" #include "status.h"
#include "statusbar.h" #include "statusbar.h"
#include "helpers/song_iterator_maker.h"
#include "utility/comparators.h" #include "utility/comparators.h"
#include "utility/functional.h"
#include "title.h" #include "title.h"
using Global::MainHeight; using Global::MainHeight;
@@ -63,12 +65,12 @@ Playlist::Playlist()
{ {
case DisplayMode::Classic: case DisplayMode::Classic:
w.setItemDisplayer(std::bind( w.setItemDisplayer(std::bind(
Display::Songs, ph::_1, proxySongList(), std::cref(Config.song_list_format) Display::Songs, ph::_1, std::cref(w), std::cref(Config.song_list_format)
)); ));
break; break;
case DisplayMode::Columns: case DisplayMode::Columns:
w.setItemDisplayer(std::bind( w.setItemDisplayer(std::bind(
Display::SongsInColumns, ph::_1, proxySongList() Display::SongsInColumns, ph::_1, std::cref(w)
)); ));
break; break;
} }
@@ -176,37 +178,9 @@ bool Playlist::find(SearchDirection direction, bool wrap, bool skip_current)
/***********************************************************************/ /***********************************************************************/
ProxySongList Playlist::proxySongList()
{
return ProxySongList(w, [](NC::Menu<MPD::Song>::Item &item) {
return &item.value();
});
}
bool Playlist::allowsSelection()
{
return !w.empty();
}
void Playlist::selectCurrent()
{
w.current()->setSelected(!w.current()->isSelected());
}
void Playlist::reverseSelection()
{
reverseSelectionHelper(w.begin(), w.end());
}
std::vector<MPD::Song> Playlist::getSelectedSongs() std::vector<MPD::Song> Playlist::getSelectedSongs()
{ {
std::vector<MPD::Song> result; return w.getSelectedSongs();
for (auto it = w.begin(); it != w.end(); ++it)
if (it->isSelected())
result.push_back(it->value());
if (result.empty() && !w.empty())
result.push_back(w.current()->value());
return result;
} }
/***********************************************************************/ /***********************************************************************/

View File

@@ -28,12 +28,13 @@
#include "regex_filter.h" #include "regex_filter.h"
#include "screen.h" #include "screen.h"
#include "song.h" #include "song.h"
#include "song_list.h"
struct Playlist: Screen<NC::Menu<MPD::Song>>, HasSongs, Searchable, Tabbable struct Playlist: Screen<SongMenu>, HasSongs, Searchable, Tabbable
{ {
Playlist(); Playlist();
// Screen<NC::Menu<MPD::Song>> implementation // Screen<SongMenu> implementation
virtual void switchTo() OVERRIDE; virtual void switchTo() OVERRIDE;
virtual void resize() OVERRIDE; virtual void resize() OVERRIDE;
@@ -56,11 +57,6 @@ struct Playlist: Screen<NC::Menu<MPD::Song>>, HasSongs, Searchable, Tabbable
virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE; virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE;
// HasSongs implementation // HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE;
virtual bool allowsSelection() OVERRIDE;
virtual void selectCurrent() OVERRIDE;
virtual void reverseSelection() OVERRIDE;
virtual std::vector<MPD::Song> getSelectedSongs() OVERRIDE; virtual std::vector<MPD::Song> getSelectedSongs() OVERRIDE;
// private members // private members

View File

@@ -34,6 +34,8 @@
#include "status.h" #include "status.h"
#include "statusbar.h" #include "statusbar.h"
#include "tag_editor.h" #include "tag_editor.h"
#include "helpers/song_iterator_maker.h"
#include "utility/functional.h"
#include "utility/comparators.h" #include "utility/comparators.h"
#include "title.h" #include "title.h"
#include "screen_switcher.h" #include "screen_switcher.h"
@@ -86,14 +88,14 @@ PlaylistEditor::PlaylistEditor()
switch (Config.playlist_editor_display_mode) switch (Config.playlist_editor_display_mode)
{ {
case DisplayMode::Classic: case DisplayMode::Classic:
Content.setItemDisplayer( Content.setItemDisplayer(std::bind(
std::bind(Display::Songs, ph::_1, contentProxyList(), std::cref(Config.song_list_format) Display::Songs, ph::_1, std::cref(Content), std::cref(Config.song_list_format)
)); ));
break; break;
case DisplayMode::Columns: case DisplayMode::Columns:
Content.setItemDisplayer( Content.setItemDisplayer(std::bind(
std::bind(Display::SongsInColumns, ph::_1, contentProxyList()) Display::SongsInColumns, ph::_1, std::cref(Content)
); ));
break; break;
} }
@@ -134,7 +136,7 @@ void PlaylistEditor::refresh()
void PlaylistEditor::switchTo() void PlaylistEditor::switchTo()
{ {
SwitchTo::execute(this); SwitchTo::execute(this);
markSongsInPlaylist(contentProxyList()); markSongsInPlaylist(Content);
drawHeader(); drawHeader();
refresh(); refresh();
} }
@@ -171,14 +173,19 @@ void PlaylistEditor::update()
MPD::SongIterator s = Mpd.GetPlaylistContent(Playlists.current()->value().path()), end; MPD::SongIterator s = Mpd.GetPlaylistContent(Playlists.current()->value().path()), end;
for (; s != end; ++s, ++idx) for (; s != end; ++s, ++idx)
{ {
bool is_bold = myPlaylist->checkForSong(*s); bool in_playlist = myPlaylist->checkForSong(*s);
if (idx < Content.size()) if (idx < Content.size())
{ {
Content[idx].setBold(is_bold); Content[idx].setBold(in_playlist);
Content[idx].value() = std::move(*s); Content[idx].value() = std::move(*s);
} }
else else
Content.addItem(std::move(*s), is_bold); {
auto properties = NC::List::Properties::Selectable;
if (in_playlist)
properties |= NC::List::Properties::Bold;
Content.addItem(std::move(*s), properties);
}
} }
if (idx < Content.size()) if (idx < Content.size())
Content.resizeList(idx); Content.resizeList(idx);
@@ -218,13 +225,6 @@ int PlaylistEditor::windowTimeout()
return Screen<WindowType>::windowTimeout(); return Screen<WindowType>::windowTimeout();
} }
ProxySongList PlaylistEditor::contentProxyList()
{
return ProxySongList(Content, [](NC::Menu<MPD::Song>::Item &item) {
return &item.value();
});
}
void PlaylistEditor::AddToPlaylist(bool add_n_play) void PlaylistEditor::AddToPlaylist(bool add_n_play)
{ {
if (isActiveWindow(Playlists) && !Playlists.empty()) if (isActiveWindow(Playlists) && !Playlists.empty())
@@ -353,36 +353,6 @@ bool PlaylistEditor::find(SearchDirection direction, bool wrap, bool skip_curren
/***********************************************************************/ /***********************************************************************/
ProxySongList PlaylistEditor::proxySongList()
{
auto ptr = ProxySongList();
if (isActiveWindow(Content))
ptr = contentProxyList();
return ptr;
}
bool PlaylistEditor::allowsSelection()
{
return (isActiveWindow(Playlists) && !Playlists.empty())
|| (isActiveWindow(Content) && !Content.empty());
}
void PlaylistEditor::selectCurrent()
{
if (isActiveWindow(Playlists))
Playlists.current()->setSelected(!Playlists.current()->isSelected());
else if (isActiveWindow(Content))
Content.current()->setSelected(!Content.current()->isSelected());
}
void PlaylistEditor::reverseSelection()
{
if (isActiveWindow(Playlists))
reverseSelectionHelper(Playlists.begin(), Playlists.end());
else if (isActiveWindow(Content))
reverseSelectionHelper(Content.begin(), Content.end());
}
std::vector<MPD::Song> PlaylistEditor::getSelectedSongs() std::vector<MPD::Song> PlaylistEditor::getSelectedSongs()
{ {
std::vector<MPD::Song> result; std::vector<MPD::Song> result;
@@ -412,14 +382,7 @@ std::vector<MPD::Song> PlaylistEditor::getSelectedSongs()
} }
} }
else if (isActiveWindow(Content)) else if (isActiveWindow(Content))
{ result = Content.getSelectedSongs();
for (auto &e : Content)
if (e.isSelected())
result.push_back(e.value());
// if no item is selected, add current one
if (result.empty() && !Content.empty())
result.push_back(Content.current()->value());
}
return result; return result;
} }

View File

@@ -26,6 +26,7 @@
#include "interfaces.h" #include "interfaces.h"
#include "regex_filter.h" #include "regex_filter.h"
#include "screen.h" #include "screen.h"
#include "song_list.h"
struct PlaylistEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tabbable struct PlaylistEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tabbable
{ {
@@ -56,11 +57,6 @@ struct PlaylistEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, T
virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE; virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE;
// HasSongs implementation // HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE;
virtual bool allowsSelection() OVERRIDE;
virtual void selectCurrent() OVERRIDE;
virtual void reverseSelection() OVERRIDE;
virtual std::vector<MPD::Song> getSelectedSongs() OVERRIDE; virtual std::vector<MPD::Song> getSelectedSongs() OVERRIDE;
// HasColumns implementation // HasColumns implementation
@@ -77,10 +73,9 @@ struct PlaylistEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, T
void requestContentsUpdate() { m_content_update_requested = true; } void requestContentsUpdate() { m_content_update_requested = true; }
virtual void Locate(const MPD::Playlist &playlist); virtual void Locate(const MPD::Playlist &playlist);
ProxySongList contentProxyList();
NC::Menu<MPD::Playlist> Playlists; NC::Menu<MPD::Playlist> Playlists;
NC::Menu<MPD::Song> Content; SongMenu Content;
private: private:
void AddToPlaylist(bool); void AddToPlaylist(bool);

View File

@@ -1,130 +0,0 @@
/***************************************************************************
* Copyright (C) 2008-2014 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. *
***************************************************************************/
#ifndef NCMPCPP_PROXY_SONG_LIST_H
#define NCMPCPP_PROXY_SONG_LIST_H
#include "menu.h"
#include "song.h"
/// This fancy class provides a way to use NC::Menu<T> template instantiations
/// with different Ts in a uniform manner. Note that since it provides methods
/// such as getSong or currentSong, it's biased towards menus that somehow
/// contain songs.
///
/// Dependent types are T and F. T is type of items stored inside Menu, whereas
/// F is a function that takes Menu<T>::Item and converts it into MPD::Song pointer.
/// For Menu<MPD::Song> this is just taking address of given MPD::Song, but e.g.
/// Menu<MPD::Item> returns not null pointer only if requested position actually
/// contains a song and not directory or playlist.
class ProxySongList
{
struct Interface
{
virtual ~Interface() { }
virtual bool empty() = 0;
virtual size_t size() = 0;
virtual size_t choice() = 0;
virtual void highlight(size_t pos) = 0;
virtual bool isSelected(size_t pos) = 0;
virtual void setSelected(size_t pos, bool selected) = 0;
virtual bool isBold(size_t pos) = 0;
virtual void setBold(size_t pos, bool bold) = 0;
virtual MPD::Song *getSong(size_t pos) = 0;
virtual MPD::Song *currentSong() = 0;
};
template <typename T, typename F> struct Impl : Interface
{
typedef typename NC::Menu<T> Menu;
Impl(Menu &menu, F f) : m_menu(menu), m_song_getter(f) { }
virtual ~Impl() { }
virtual bool empty() { return m_menu.empty(); }
virtual size_t size() { return m_menu.size(); }
virtual size_t choice() { return m_menu.choice(); }
virtual void highlight(size_t pos) { m_menu.highlight(pos); }
virtual bool isSelected(size_t pos) {
assert(pos < m_menu.size());
return m_menu[pos].isSelected();
}
virtual void setSelected(size_t pos, bool selected) {
assert(pos < m_menu.size());
m_menu[pos].setSelected(selected);
}
virtual bool isBold(size_t pos) {
assert(pos < m_menu.size());
return m_menu[pos].isBold();
}
virtual void setBold(size_t pos, bool bold) {
assert(pos < m_menu.size());
m_menu[pos].setBold(bold);
}
virtual MPD::Song *getSong(size_t pos) {
assert(pos < m_menu.size());
return m_song_getter(m_menu[pos]);
}
virtual MPD::Song *currentSong() {
if (!m_menu.empty())
return getSong(m_menu.choice());
else
return 0;
}
private:
Menu &m_menu;
F m_song_getter;
};
std::shared_ptr<Interface> m_impl;
public:
ProxySongList() { }
template <typename T, typename F>
ProxySongList(typename NC::Menu<T> &menu, F f) : m_impl(new Impl<T, F>(menu, f)) { }
bool empty() const { return m_impl->empty(); }
size_t size() const { return m_impl->size(); }
size_t choice() const { return m_impl->choice(); }
void highlight(size_t pos) const { m_impl->highlight(pos); }
bool isSelected(size_t pos) const { return m_impl->isSelected(pos); }
void setSelected(size_t pos, bool selected) const { m_impl->setSelected(pos, selected); }
bool isBold(size_t pos) const { return m_impl->isBold(pos); }
void setBold(size_t pos, bool bold) const{ m_impl->setBold(pos, bold); }
MPD::Song *getSong(size_t pos) const { return m_impl->getSong(pos); }
MPD::Song *currentSong() const { return m_impl->currentSong(); }
/// @return true if there is no underlying menu object, false otherwise
operator bool() const { return m_impl.get() != 0; }
};
#endif // NCMPCPP_PROXY_SONG_LIST_H

View File

@@ -38,10 +38,11 @@ struct BaseScreen
virtual ~BaseScreen() { } virtual ~BaseScreen() { }
/// @see Screen::isActiveWindow() /// @see Screen::isActiveWindow()
virtual bool isActiveWindow(const NC::Window &w_) = 0; virtual bool isActiveWindow(const NC::Window &w_) const = 0;
/// @see Screen::activeWindow() /// @see Screen::activeWindow()
virtual void *activeWindow() = 0; virtual NC::Window *activeWindow() = 0;
virtual const NC::Window *activeWindow() const = 0;
/// @see Screen::refresh() /// @see Screen::refresh()
virtual void refresh() = 0; virtual void refresh() = 0;
@@ -123,19 +124,26 @@ template <typename WindowT> struct Screen : public BaseScreen
{ {
typedef WindowT WindowType; typedef WindowT WindowType;
typedef typename std::add_lvalue_reference<WindowType>::type WindowReference; typedef typename std::add_lvalue_reference<WindowType>::type WindowReference;
typedef typename std::add_lvalue_reference<
typename std::add_const<WindowType>::type
>::type ConstWindowReference;
private: private:
template <bool IsPointer, typename Result> struct getObject { }; template <bool IsPointer, typename Result, typename ConstResult>
template <typename Result> struct getObject<true, Result> { struct getObject {
static Result apply(WindowType w) { return *w; } static Result &apply(WindowReference w) { return w; }
static ConstResult &constApply(ConstWindowReference w) { return w; }
}; };
template <typename Result> struct getObject<false, Result> { template <typename Result, typename ConstResult>
static Result apply(WindowReference w) { return w; } struct getObject<true, Result, ConstResult> {
static Result &apply(WindowType w) { return *w; }
static ConstResult &constApply(const WindowType w) { return *w; }
}; };
typedef getObject< typedef getObject<
std::is_pointer<WindowT>::value, std::is_pointer<WindowT>::value,
typename std::add_lvalue_reference< typename std::remove_pointer<WindowT>::type,
typename std::add_const<
typename std::remove_pointer<WindowT>::type typename std::remove_pointer<WindowT>::type
>::type >::type
> Accessor; > Accessor;
@@ -146,17 +154,20 @@ public:
virtual ~Screen() { } virtual ~Screen() { }
virtual bool isActiveWindow(const NC::Window &w_) OVERRIDE { virtual bool isActiveWindow(const NC::Window &w_) const OVERRIDE {
return &Accessor::apply(w) == &w_; return &Accessor::constApply(w) == &w_;
} }
/// Since some screens contain more that one window /// Since some screens contain more that one window
/// it's useful to determine the one that is being /// it's useful to determine the one that is being
/// active /// active
/// @return address to window object cast to void * /// @return address to window object cast to void *
virtual void *activeWindow() OVERRIDE { virtual NC::Window *activeWindow() OVERRIDE {
return &Accessor::apply(w); return &Accessor::apply(w);
} }
virtual const NC::Window *activeWindow() const OVERRIDE {
return &Accessor::constApply(w);
}
/// Refreshes whole screen /// Refreshes whole screen
virtual void refresh() OVERRIDE { virtual void refresh() OVERRIDE {

View File

@@ -31,6 +31,7 @@
#include "settings.h" #include "settings.h"
#include "status.h" #include "status.h"
#include "statusbar.h" #include "statusbar.h"
#include "helpers/song_iterator_maker.h"
#include "utility/comparators.h" #include "utility/comparators.h"
#include "title.h" #include "title.h"
#include "screen_switcher.h" #include "screen_switcher.h"
@@ -74,8 +75,63 @@ namespace pos {
std::string SEItemToString(const SEItem &ei); std::string SEItemToString(const SEItem &ei);
bool SEItemEntryMatcher(const Regex::Regex &rx, const NC::Menu<SEItem>::Item &item, bool filter); bool SEItemEntryMatcher(const Regex::Regex &rx, const NC::Menu<SEItem>::Item &item, bool filter);
template <bool Const>
struct SongExtractor
{
typedef SongExtractor type;
typedef typename NC::Menu<SEItem>::Item MenuItem;
typedef typename std::conditional<Const, const MenuItem, MenuItem>::type Item;
typedef typename std::conditional<Const, const MPD::Song, MPD::Song>::type Song;
Song *operator()(Item &item) const
{
Song *ptr = nullptr;
if (!item.isSeparator() && item.value().isSong())
ptr = &item.value().song();
return ptr;
}
};
} }
SongIterator SearchEngineWindow::currentS()
{
return makeSongIterator_<SEItem>(current(), SongExtractor<false>());
}
ConstSongIterator SearchEngineWindow::currentS() const
{
return makeConstSongIterator_<SEItem>(current(), SongExtractor<true>());
}
SongIterator SearchEngineWindow::beginS()
{
return makeSongIterator_<SEItem>(begin(), SongExtractor<false>());
}
ConstSongIterator SearchEngineWindow::beginS() const
{
return makeConstSongIterator_<SEItem>(begin(), SongExtractor<true>());
}
SongIterator SearchEngineWindow::endS()
{
return makeSongIterator_<SEItem>(end(), SongExtractor<false>());
}
ConstSongIterator SearchEngineWindow::endS() const
{
return makeConstSongIterator_<SEItem>(end(), SongExtractor<true>());
}
std::vector<MPD::Song> SearchEngineWindow::getSelectedSongs()
{
return {}; // TODO
}
/**********************************************************************/
const char *SearchEngine::ConstraintsNames[] = const char *SearchEngine::ConstraintsNames[] =
{ {
"Any", "Any",
@@ -109,7 +165,7 @@ SearchEngine::SearchEngine()
w.setHighlightColor(Config.main_highlight_color); w.setHighlightColor(Config.main_highlight_color);
w.cyclicScrolling(Config.use_cyclic_scrolling); w.cyclicScrolling(Config.use_cyclic_scrolling);
w.centeredCursor(Config.centered_cursor); w.centeredCursor(Config.centered_cursor);
w.setItemDisplayer(std::bind(Display::SEItems, ph::_1, proxySongList())); w.setItemDisplayer(std::bind(Display::SEItems, ph::_1, std::cref(w)));
w.setSelectedPrefix(Config.selected_item_prefix); w.setSelectedPrefix(Config.selected_item_prefix);
w.setSelectedSuffix(Config.selected_item_suffix); w.setSelectedSuffix(Config.selected_item_suffix);
SearchMode = &SearchModes[Config.search_engine_default_search_mode]; SearchMode = &SearchModes[Config.search_engine_default_search_mode];
@@ -140,7 +196,7 @@ void SearchEngine::switchTo()
SwitchTo::execute(this); SwitchTo::execute(this);
if (w.empty()) if (w.empty())
Prepare(); Prepare();
markSongsInPlaylist(proxySongList()); markSongsInPlaylist(w);
drawHeader(); drawHeader();
} }
@@ -190,10 +246,10 @@ void SearchEngine::enterPressed()
size_t found = w.size()-SearchEngine::StaticOptions; size_t found = w.size()-SearchEngine::StaticOptions;
found += 3; // don't count options inserted below found += 3; // don't count options inserted below
w.insertSeparator(ResetButton+1); w.insertSeparator(ResetButton+1);
w.insertItem(ResetButton+2, SEItem(), 1, 1); w.insertItem(ResetButton+2, SEItem(), NC::List::Properties::Bold | NC::List::Properties::Inactive);
w.at(ResetButton+2).value().mkBuffer() << Config.color1 << "Search results: " << Config.color2 << "Found " << found << (found > 1 ? " songs" : " song") << NC::Color::Default; w.at(ResetButton+2).value().mkBuffer() << Config.color1 << "Search results: " << Config.color2 << "Found " << found << (found > 1 ? " songs" : " song") << NC::Color::Default;
w.insertSeparator(ResetButton+3); w.insertSeparator(ResetButton+3);
markSongsInPlaylist(proxySongList()); markSongsInPlaylist(w);
Statusbar::print("Searching finished"); Statusbar::print("Searching finished");
if (Config.block_search_constraints_change) if (Config.block_search_constraints_change)
for (size_t i = 0; i < StaticOptions-4; ++i) for (size_t i = 0; i < StaticOptions-4; ++i)
@@ -276,31 +332,6 @@ bool SearchEngine::find(SearchDirection direction, bool wrap, bool skip_current)
/***********************************************************************/ /***********************************************************************/
ProxySongList SearchEngine::proxySongList()
{
return ProxySongList(w, [](NC::Menu<SEItem>::Item &item) -> MPD::Song * {
MPD::Song *ptr = 0;
if (!item.isSeparator() && item.value().isSong())
ptr = &item.value().song();
return ptr;
});
}
bool SearchEngine::allowsSelection()
{
return w.current()->value().isSong();
}
void SearchEngine::selectCurrent()
{
w.current()->setSelected(!w.current()->isSelected());
}
void SearchEngine::reverseSelection()
{
reverseSelectionHelper(w.begin()+std::min(StaticOptions, w.size()), w.end());
}
std::vector<MPD::Song> SearchEngine::getSelectedSongs() std::vector<MPD::Song> SearchEngine::getSelectedSongs()
{ {
std::vector<MPD::Song> result; std::vector<MPD::Song> result;
@@ -328,6 +359,9 @@ void SearchEngine::Prepare()
w.setTitle(""); w.setTitle("");
w.clear(); w.clear();
w.resizeList(StaticOptions-3); w.resizeList(StaticOptions-3);
for (auto &item : w)
item.setSelectable(false);
w.at(ConstraintsNumber).setSeparator(true); w.at(ConstraintsNumber).setSeparator(true);
w.at(SearchButton-1).setSeparator(true); w.at(SearchButton-1).setSeparator(true);

View File

@@ -27,6 +27,7 @@
#include "mpdpp.h" #include "mpdpp.h"
#include "regex_filter.h" #include "regex_filter.h"
#include "screen.h" #include "screen.h"
#include "song_list.h"
struct SEItem struct SEItem
{ {
@@ -74,11 +75,27 @@ private:
MPD::Song m_song; MPD::Song m_song;
}; };
struct SearchEngine: Screen<NC::Menu<SEItem>>, HasSongs, Searchable, Tabbable struct SearchEngineWindow: NC::Menu<SEItem>, SongList
{
SearchEngineWindow() { }
SearchEngineWindow(NC::Menu<SEItem> &&base)
: NC::Menu<SEItem>(std::move(base)) { }
virtual SongIterator currentS() OVERRIDE;
virtual ConstSongIterator currentS() const OVERRIDE;
virtual SongIterator beginS() OVERRIDE;
virtual ConstSongIterator beginS() const OVERRIDE;
virtual SongIterator endS() OVERRIDE;
virtual ConstSongIterator endS() const OVERRIDE;
virtual std::vector<MPD::Song> getSelectedSongs() OVERRIDE;
};
struct SearchEngine: Screen<SearchEngineWindow>, HasSongs, Searchable, Tabbable
{ {
SearchEngine(); SearchEngine();
// Screen< NC::Menu<SEItem> > implementation // Screen<SearchEngineWindow> implementation
virtual void resize() OVERRIDE; virtual void resize() OVERRIDE;
virtual void switchTo() OVERRIDE; virtual void switchTo() OVERRIDE;
@@ -101,11 +118,6 @@ struct SearchEngine: Screen<NC::Menu<SEItem>>, HasSongs, Searchable, Tabbable
virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE; virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE;
// HasSongs implementation // HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE;
virtual bool allowsSelection() OVERRIDE;
virtual void selectCurrent() OVERRIDE;
virtual void reverseSelection() OVERRIDE;
virtual std::vector<MPD::Song> getSelectedSongs() OVERRIDE; virtual std::vector<MPD::Song> getSelectedSongs() OVERRIDE;
// private members // private members

View File

@@ -106,14 +106,14 @@ void SelectedItemsAdder::switchTo()
using Global::myScreen; using Global::myScreen;
auto hs = hasSongs(myScreen); auto hs = hasSongs(myScreen);
if (!hs || !hs->allowsSelection()) if (!hs)
return; return;
Statusbar::print(1, "Fetching selected songs..."); Statusbar::print(1, "Fetching selected songs...");
m_selected_items = hs->getSelectedSongs(); m_selected_items = hs->getSelectedSongs();
if (m_selected_items.empty()) if (m_selected_items.empty())
{ {
Statusbar::print("List of selected items is empty"); Statusbar::print("No selected songs");
return; return;
} }
populatePlaylistSelector(myScreen); populatePlaylistSelector(myScreen);

View File

@@ -93,7 +93,7 @@ void SongInfo::switchTo()
switchToPreviousScreen(); switchToPreviousScreen();
} }
void SongInfo::PrepareSong(MPD::Song &s) void SongInfo::PrepareSong(const MPD::Song &s)
{ {
w << NC::Format::Bold << Config.color1 << "Filename: " << NC::Format::NoBold << Config.color2 << s.getName() << '\n' << NC::Color::End; w << NC::Format::Bold << Config.color1 << "Filename: " << NC::Format::NoBold << Config.color2 << s.getName() << '\n' << NC::Color::End;
w << NC::Format::Bold << "Directory: " << NC::Format::NoBold << Config.color2; w << NC::Format::Bold << "Directory: " << NC::Format::NoBold << Config.color2;

View File

@@ -55,7 +55,7 @@ struct SongInfo: Screen<NC::Scrollpad>, Tabbable
static const Metadata Tags[]; static const Metadata Tags[];
private: private:
void PrepareSong(MPD::Song &); void PrepareSong(const MPD::Song &s);
}; };
extern SongInfo *mySongInfo; extern SongInfo *mySongInfo;

83
src/song_list.cpp Normal file
View File

@@ -0,0 +1,83 @@
/***************************************************************************
* Copyright (C) 2008-2014 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 "helpers/song_iterator_maker.h"
#include "song_info.h"
#include "utility/functional.h"
namespace {
template <bool Const>
struct SongExtractor
{
typedef SongExtractor type;
typedef typename NC::Menu<MPD::Song>::Item MenuItem;
typedef typename std::conditional<Const, const MenuItem, MenuItem>::type Item;
typedef typename std::conditional<Const, const MPD::Song, MPD::Song>::type Song;
Song *operator()(Item &item) const
{
return &item.value();
}
};
}
SongIterator SongMenu::currentS()
{
return makeSongIterator_<MPD::Song>(current(), SongExtractor<false>());
}
ConstSongIterator SongMenu::currentS() const
{
return makeConstSongIterator_<MPD::Song>(current(), SongExtractor<true>());
}
SongIterator SongMenu::beginS()
{
return makeSongIterator_<MPD::Song>(begin(), SongExtractor<false>());
}
ConstSongIterator SongMenu::beginS() const
{
return makeConstSongIterator_<MPD::Song>(begin(), SongExtractor<true>());
}
SongIterator SongMenu::endS()
{
return makeSongIterator_<MPD::Song>(end(), SongExtractor<false>());
}
ConstSongIterator SongMenu::endS() const
{
return makeConstSongIterator_<MPD::Song>(end(), SongExtractor<true>());
}
std::vector<MPD::Song> SongMenu::getSelectedSongs()
{
std::vector<MPD::Song> result;
for (auto it = begin(); it != end(); ++it)
if (it->isSelected())
result.push_back(it->value());
if (result.empty() && !empty())
result.push_back(current()->value());
return result;
}

View File

@@ -450,17 +450,17 @@ void Status::Changes::playlist(unsigned previous_version)
myPlaylist->reloadRemaining(); myPlaylist->reloadRemaining();
if (isVisible(myBrowser)) if (isVisible(myBrowser))
markSongsInPlaylist(myBrowser->proxySongList()); markSongsInPlaylist(myBrowser->main());
if (isVisible(mySearcher)) if (isVisible(mySearcher))
markSongsInPlaylist(mySearcher->proxySongList()); markSongsInPlaylist(mySearcher->main());
if (isVisible(myLibrary)) if (isVisible(myLibrary))
{ {
markSongsInPlaylist(myLibrary->songsProxyList()); markSongsInPlaylist(myLibrary->Songs);
myLibrary->Songs.refresh(); myLibrary->Songs.refresh();
} }
if (isVisible(myPlaylistEditor)) if (isVisible(myPlaylistEditor))
{ {
markSongsInPlaylist(myPlaylistEditor->contentProxyList()); markSongsInPlaylist(myPlaylistEditor->Content);
myPlaylistEditor->Content.refresh(); myPlaylistEditor->Content.refresh();
} }
} }

View File

@@ -36,6 +36,8 @@
#include "playlist.h" #include "playlist.h"
#include "song_info.h" #include "song_info.h"
#include "statusbar.h" #include "statusbar.h"
#include "helpers/song_iterator_maker.h"
#include "utility/functional.h"
#include "utility/comparators.h" #include "utility/comparators.h"
#include "title.h" #include "title.h"
#include "tags.h" #include "tags.h"
@@ -85,8 +87,60 @@ std::string SongToString(const MPD::MutableSong &s);
bool DirEntryMatcher(const Regex::Regex &rx, const std::pair<std::string, std::string> &dir, bool filter); bool DirEntryMatcher(const Regex::Regex &rx, const std::pair<std::string, std::string> &dir, bool filter);
bool SongEntryMatcher(const Regex::Regex &rx, const MPD::MutableSong &s); bool SongEntryMatcher(const Regex::Regex &rx, const MPD::MutableSong &s);
template <bool Const>
struct SongExtractor
{
typedef SongExtractor type;
typedef typename NC::Menu<MPD::MutableSong>::Item MenuItem;
typedef typename std::conditional<Const, const MenuItem, MenuItem>::type Item;
typedef typename std::conditional<Const, const MPD::Song, MPD::Song>::type Song;
Song *operator()(Item &item) const
{
return &item.value();
}
};
} }
SongIterator TagsWindow::currentS()
{
return makeSongIterator_<MPD::MutableSong>(current(), SongExtractor<false>());
}
ConstSongIterator TagsWindow::currentS() const
{
return makeConstSongIterator_<MPD::MutableSong>(current(), SongExtractor<true>());
}
SongIterator TagsWindow::beginS()
{
return makeSongIterator_<MPD::MutableSong>(begin(), SongExtractor<false>());
}
ConstSongIterator TagsWindow::beginS() const
{
return makeConstSongIterator_<MPD::MutableSong>(begin(), SongExtractor<true>());
}
SongIterator TagsWindow::endS()
{
return makeSongIterator_<MPD::MutableSong>(end(), SongExtractor<false>());
}
ConstSongIterator TagsWindow::endS() const
{
return makeConstSongIterator_<MPD::MutableSong>(end(), SongExtractor<true>());
}
std::vector<MPD::Song> TagsWindow::getSelectedSongs()
{
return {}; // TODO
}
/**********************************************************************/
TagEditor::TagEditor() : FParser(0), FParserHelper(0), FParserLegend(0), FParserPreview(0), itsBrowsedDir("/") TagEditor::TagEditor() : FParser(0), FParserHelper(0), FParserLegend(0), FParserPreview(0), itsBrowsedDir("/")
{ {
PatternsFile = Config.ncmpcpp_directory + "patterns.list"; PatternsFile = Config.ncmpcpp_directory + "patterns.list";
@@ -115,7 +169,7 @@ TagEditor::TagEditor() : FParser(0), FParserHelper(0), FParserLegend(0), FParser
TagTypes->addSeparator(); TagTypes->addSeparator();
if (Config.titles_visibility) if (Config.titles_visibility)
{ {
TagTypes->addItem("Options", 1, 1); TagTypes->addItem("Options", NC::List::Properties::Bold | NC::List::Properties::Inactive);
TagTypes->addSeparator(); TagTypes->addSeparator();
} }
TagTypes->addItem("Capitalize First Letters"); TagTypes->addItem("Capitalize First Letters");
@@ -124,7 +178,7 @@ TagEditor::TagEditor() : FParser(0), FParserHelper(0), FParserLegend(0), FParser
TagTypes->addItem("Reset"); TagTypes->addItem("Reset");
TagTypes->addItem("Save"); TagTypes->addItem("Save");
Tags = new NC::Menu<MPD::MutableSong>(RightColumnStartX, MainStartY, RightColumnWidth, MainHeight, Config.titles_visibility ? "Tags" : "", Config.main_color, NC::Border()); Tags = new TagsWindow(NC::Menu<MPD::MutableSong>(RightColumnStartX, MainStartY, RightColumnWidth, MainHeight, Config.titles_visibility ? "Tags" : "", Config.main_color, NC::Border()));
Tags->setHighlightColor(Config.main_highlight_color); Tags->setHighlightColor(Config.main_highlight_color);
Tags->cyclicScrolling(Config.use_cyclic_scrolling); Tags->cyclicScrolling(Config.use_cyclic_scrolling);
Tags->centeredCursor(Config.centered_cursor); Tags->centeredCursor(Config.centered_cursor);
@@ -337,7 +391,7 @@ void TagEditor::enterPressed()
if (!Patterns.empty()) if (!Patterns.empty())
{ {
FParser->addSeparator(); FParser->addSeparator();
FParser->addItem("Recent patterns", 1, 1); FParser->addItem("Recent patterns", NC::List::Properties::Bold | NC::List::Properties::Inactive);
FParser->addSeparator(); FParser->addSeparator();
for (std::list<std::string>::const_iterator it = Patterns.begin(); it != Patterns.end(); ++it) for (std::list<std::string>::const_iterator it = Patterns.begin(); it != Patterns.end(); ++it)
FParser->addItem(*it); FParser->addItem(*it);
@@ -752,31 +806,6 @@ bool TagEditor::find(SearchDirection direction, bool wrap, bool skip_current)
/***********************************************************************/ /***********************************************************************/
ProxySongList TagEditor::proxySongList()
{
auto ptr = ProxySongList();
if (w == Tags)
ptr = ProxySongList(*Tags, [](NC::Menu<MPD::MutableSong>::Item &item) {
return &item.value();
});
return ptr;
}
bool TagEditor::allowsSelection()
{
return w == Tags && !Tags->empty();
}
void TagEditor::selectCurrent()
{
Tags->current()->setSelected(!Tags->current()->isSelected());
}
void TagEditor::reverseSelection()
{
reverseSelectionHelper(Tags->begin(), Tags->end());
}
std::vector<MPD::Song> TagEditor::getSelectedSongs() std::vector<MPD::Song> TagEditor::getSelectedSongs()
{ {
std::vector<MPD::Song> result; std::vector<MPD::Song> result;

View File

@@ -31,6 +31,23 @@
#include "mutable_song.h" #include "mutable_song.h"
#include "regex_filter.h" #include "regex_filter.h"
#include "screen.h" #include "screen.h"
#include "song_list.h"
struct TagsWindow: NC::Menu<MPD::MutableSong>, SongList
{
TagsWindow() { }
TagsWindow(NC::Menu<MPD::MutableSong> &&base)
: NC::Menu<MPD::MutableSong>(std::move(base)) { }
virtual SongIterator currentS() OVERRIDE;
virtual ConstSongIterator currentS() const OVERRIDE;
virtual SongIterator beginS() OVERRIDE;
virtual ConstSongIterator beginS() const OVERRIDE;
virtual SongIterator endS() OVERRIDE;
virtual ConstSongIterator endS() const OVERRIDE;
virtual std::vector<MPD::Song> getSelectedSongs() OVERRIDE;
};
struct TagEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tabbable struct TagEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tabbable
{ {
@@ -59,11 +76,6 @@ struct TagEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tabbab
virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE; virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE;
// HasSongs implementation // HasSongs implementation
virtual ProxySongList proxySongList() OVERRIDE;
virtual bool allowsSelection() OVERRIDE;
virtual void selectCurrent() OVERRIDE;
virtual void reverseSelection() OVERRIDE;
virtual std::vector<MPD::Song> getSelectedSongs() OVERRIDE; virtual std::vector<MPD::Song> getSelectedSongs() OVERRIDE;
// HasColumns implementation // HasColumns implementation
@@ -79,7 +91,7 @@ struct TagEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tabbab
NC::Menu< std::pair<std::string, std::string> > *Dirs; NC::Menu< std::pair<std::string, std::string> > *Dirs;
NC::Menu<std::string> *TagTypes; NC::Menu<std::string> *TagTypes;
NC::Menu<MPD::MutableSong> *Tags; TagsWindow *Tags;
private: private:
void SetDimensions(size_t, size_t); void SetDimensions(size_t, size_t);

View File

@@ -24,6 +24,13 @@
#include <boost/locale/encoding_utf.hpp> #include <boost/locale/encoding_utf.hpp>
#include <utility> #include <utility>
template <typename ValueT>
struct pointer_extractor
{
typedef pointer_extractor type;
ValueT *operator()(ValueT &v) const { return &v; }
};
// identity function object // identity function object
struct id_ struct id_
{ {