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

View File

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

View File

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

View File

@@ -38,6 +38,7 @@
#include "tag_editor.h"
#include "title.h"
#include "tags.h"
#include "helpers/song_iterator_maker.h"
#include "utility/comparators.h"
#include "utility/string.h"
#include "configuration.h"
@@ -69,8 +70,63 @@ void clearDirectory(const std::string &directory);
std::string itemToString(const MPD::Item &item);
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()
: m_update_request(true)
, m_local_browser(false)
@@ -83,7 +139,7 @@ Browser::Browser()
w.centeredCursor(Config.centered_cursor);
w.setSelectedPrefix(Config.selected_item_prefix);
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()
@@ -110,7 +166,7 @@ void Browser::resize()
void Browser::switchTo()
{
SwitchTo::execute(this);
markSongsInPlaylist(proxySongList());
markSongsInPlaylist(w);
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> songs;
@@ -446,7 +474,7 @@ void Browser::getDirectory(std::string directory)
if (!isRootDirectory(directory))
{
// 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)
@@ -468,8 +496,10 @@ void Browser::getDirectory(std::string directory)
}
case MPD::Item::Type::Song:
{
bool is_bold = myPlaylist->checkForSong(item.song());
w.addItem(std::move(item), is_bold);
auto properties = NC::List::Properties::Selectable;
if (myPlaylist->checkForSong(item.song()))
properties |= NC::List::Properties::Bold;
w.addItem(std::move(item), properties);
break;
}
}

View File

@@ -25,12 +25,29 @@
#include "mpdpp.h"
#include "regex_filter.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();
// Screen< NC::Menu<MPD::Item> > implementation
// Screen<BrowserWindow> implementation
virtual void resize() 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;
// 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;
// private members

View File

@@ -77,27 +77,30 @@ const wchar_t *toColumnName(char c)
}
template <typename T>
void setProperties(NC::Menu<T> &menu, const MPD::Song &s, const ProxySongList &pl, bool &separate_albums,
bool &is_now_playing, bool &is_selected, bool &discard_colors)
void setProperties(NC::Menu<T> &menu, const MPD::Song &s, const SongList &list,
bool &separate_albums, bool &is_now_playing, bool &is_selected, bool &discard_colors)
{
size_t drawn_pos = menu.drawn() - menu.begin();
separate_albums = false;
if (Config.playlist_separate_albums)
{
size_t next_pos = drawn_pos+1;
auto next = next_pos < pl.size() ? pl.getSong(next_pos) : 0;
if (next && next->getAlbum() != s.getAlbum())
separate_albums = true;
auto next = list.beginS() + drawn_pos + 1;
if (next != list.endS())
{
auto next_s = next->get<Bit::Song>();
if (next_s != nullptr && next_s->getAlbum() != s.getAlbum())
separate_albums = true;
}
}
if (separate_albums)
{
menu << NC::Format::Underline;
mvwhline(menu.raw(), menu.getY(), 0, KEY_SPACE, menu.getWidth());
}
is_selected = menu.drawn()->isSelected();
discard_colors = Config.discard_colors_if_item_is_selected && is_selected;
int song_pos = drawn_pos;
is_now_playing = Status::State::player() != MPD::psStop && myPlaylist->isActiveWindow(menu)
&& 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>
void showSongs(NC::Menu<T> &menu, const MPD::Song &s,
const ProxySongList &pl, const Format::AST<char> &ast)
void showSongs(NC::Menu<T> &menu, const MPD::Song &s, const SongList &list, const Format::AST<char> &ast)
{
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();
NC::Buffer right_aligned;
@@ -134,14 +136,14 @@ void showSongs(NC::Menu<T> &menu, const MPD::Song &s,
}
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())
return;
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 y = menu.getY();
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.
if (it != last)
--width;
if (it == Config.columns.begin() && (is_now_playing || is_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;
remained_width -= offset;
}
// 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 (*) */)
break;
std::wstring tag;
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)
tag = ToWString(Config.empty_tag);
wideCut(tag, width);
if (!discard_colors && it->color != NC::Color::Default)
menu << it->color;
int x_off = 0;
// if column uses right alignment, calculate proper offset.
// otherwise just assume offset is 0, ie. we start from the left.
if (it->right_alignment)
x_off = std::max(0, width - int(wideLength(tag)));
whline(menu.raw(), KEY_SPACE, width);
menu.goToXY(x + x_off, y);
menu << tag;
@@ -224,11 +226,11 @@ void showSongsInColumns(NC::Menu<T> &menu, const MPD::Song &s, const ProxySongLi
menu << ' ';
remained_width -= width+1;
}
if (!discard_colors && it->color != NC::Color::Default)
menu << NC::Color::End;
}
// here comes the shitty part, second chapter. here we apply
// now playing suffix or/and make room for selected suffix
// (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)
menu.goToXY(menu.getWidth() - Config.selected_item_suffix_length, y);
if (separate_albums)
menu << NC::Format::NoUnderline;
}
@@ -320,15 +322,14 @@ std::string Display::Columns(size_t list_width)
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,
const ProxySongList &pl, const Format::AST<char> &ast)
void Display::Songs(NC::Menu<MPD::Song> &menu, const SongList &list, const Format::AST<char> &ast)
{
showSongs(menu, menu.drawn()->value(), pl, ast);
showSongs(menu, menu.drawn()->value(), list, ast);
}
#ifdef HAVE_TAGLIB_H
@@ -354,7 +355,7 @@ void Display::Tags(NC::Menu<MPD::MutableSong> &menu)
}
#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();
switch (item.type())
@@ -368,10 +369,10 @@ void Display::Items(NC::Menu<MPD::Item> &menu, const ProxySongList &pl)
switch (Config.browser_display_mode)
{
case DisplayMode::Classic:
showSongs(menu, item.song(), pl, Config.song_list_format);
showSongs(menu, item.song(), list, Config.song_list_format);
break;
case DisplayMode::Columns:
showSongsInColumns(menu, item.song(), pl);
showSongsInColumns(menu, item.song(), list);
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();
if (si.isSong())
@@ -390,10 +391,10 @@ void Display::SEItems(NC::Menu<SEItem> &menu, const ProxySongList &pl)
switch (Config.search_engine_display_mode)
{
case DisplayMode::Classic:
showSongs(menu, si.song(), pl, Config.song_list_format);
showSongs(menu, si.song(), list, Config.song_list_format);
break;
case DisplayMode::Columns:
showSongsInColumns(menu, si.song(), pl);
showSongsInColumns(menu, si.song(), list);
break;
}
}

View File

@@ -26,20 +26,21 @@
#include "menu.h"
#include "mutable_song.h"
#include "search_engine.h"
#include "song_list.h"
namespace Display {
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 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 "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 result = false;
@@ -81,12 +144,15 @@ std::string Timestamp(time_t t)
return result;
}
void markSongsInPlaylist(ProxySongList pl)
void markSongsInPlaylist(SongList &list)
{
size_t list_size = pl.size();
for (size_t i = 0; i < list_size; ++i)
if (auto s = pl.getSong(i))
pl.setBold(i, myPlaylist->checkForSong(*s));
MPD::Song *s;
for (auto &p : list)
{
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)

View File

@@ -25,6 +25,7 @@
#include "mpdpp.h"
#include "screen.h"
#include "settings.h"
#include "song_list.h"
#include "status.h"
#include "utility/string.h"
#include "utility/type_conversions.h"
@@ -93,24 +94,6 @@ inline HasSongs *hasSongs(BaseScreen *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>
bool hasSelected(Iterator first, Iterator last)
{
@@ -314,12 +297,6 @@ void cropPlaylist(NC::Menu<MPD::Song> &m, F 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)
{
assert(first != last);
@@ -384,68 +361,25 @@ template <typename BufferT> void ShowTag(BufferT &buf, const std::string &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)
{
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);
const MPD::Song *currentSong(const BaseScreen *screen);
std::string timeFormat(const char *format, 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);
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
#define NCMPCPP_INTERFACES_H
#include <boost/range/detail/any_iterator.hpp>
#include <boost/tuple/tuple.hpp>
#include <string>
#include "enums.h"
#include "gcc.h"
#include "screen.h"
#include "song.h"
#include "proxy_song_list.h"
struct Searchable
{
@@ -38,11 +39,6 @@ struct Searchable
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;
};

View File

@@ -34,7 +34,9 @@
#include "media_library.h"
#include "status.h"
#include "statusbar.h"
#include "helpers/song_iterator_maker.h"
#include "utility/comparators.h"
#include "utility/functional.h"
#include "utility/type_conversions.h"
#include "title.h"
#include "screen_switcher.h"
@@ -202,7 +204,7 @@ MediaLibrary::MediaLibrary()
Songs.setSelectedPrefix(Config.selected_item_prefix);
Songs.setSelectedSuffix(Config.selected_item_suffix);
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;
@@ -257,7 +259,7 @@ void MediaLibrary::refresh()
void MediaLibrary::switchTo()
{
SwitchTo::execute(this);
markSongsInPlaylist(songsProxyList());
markSongsInPlaylist(Songs);
drawHeader();
refresh();
}
@@ -418,14 +420,19 @@ void MediaLibrary::update()
size_t idx = 0;
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())
{
Songs[idx].value() = std::move(*s);
Songs[idx].setBold(is_playlist);
Songs[idx].setBold(in_playlist);
}
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())
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> result;
@@ -717,14 +674,7 @@ std::vector<MPD::Song> MediaLibrary::getSelectedSongs()
}
}
else if (isActiveWindow(Songs))
{
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());
}
Songs.getSelectedSongs();
return result;
}
@@ -837,13 +787,6 @@ int MediaLibrary::Columns()
return 3;
}
ProxySongList MediaLibrary::songsProxyList()
{
return ProxySongList(Songs, [](NC::Menu<MPD::Song>::Item &item) {
return &item.value();
});
}
void MediaLibrary::toggleSortMode()
{
Config.media_library_sort_by_mtime = !Config.media_library_sort_by_mtime;

View File

@@ -26,6 +26,7 @@
#include "interfaces.h"
#include "regex_filter.h"
#include "screen.h"
#include "song_list.h"
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;
// 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;
// HasColumns implementation
@@ -75,7 +71,6 @@ struct MediaLibrary: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tab
void toggleColumnsMode();
int Columns();
void LocateSong(const MPD::Song &);
ProxySongList songsProxyList();
void toggleSortMode();
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<AlbumEntry> Albums;
NC::Menu<MPD::Song> Songs;
SongMenu Songs;
private:
void AddToPlaylist(bool);

View File

@@ -22,6 +22,8 @@
#define NCMPCPP_MENU_H
#include <boost/iterator/indirect_iterator.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/range/detail/any_iterator.hpp>
#include <cassert>
#include <functional>
#include <iterator>
@@ -33,21 +35,150 @@
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
/// holding any std::vector compatible values.
template <typename ItemT> class Menu : public Window
template <typename ItemT> struct Menu : Window, List
{
public:
struct Item
struct Item : List::Properties
{
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;
friend class Menu<ItemT>;
friend struct Menu<ItemT>;
Item()
: m_is_bold(false), m_is_selected(false), m_is_inactive(false), m_is_separator(false) { }
Item(ItemT value_, bool is_bold, bool is_inactive)
: m_value(value_), m_is_bold(is_bold), m_is_selected(false), m_is_inactive(is_inactive), m_is_separator(false) { }
Item() { }
Item(ItemT value_, Properties::Type properties)
: Properties(properties)
, m_value(value_)
{ }
ItemT &value() { return m_value; }
const ItemT &value() const { return m_value; }
@@ -55,29 +186,16 @@ public:
ItemT &operator*() { 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:
static Item mkSeparator()
{
Item item;
item.m_is_separator = true;
item.setSelectable(false);
item.setSeparator(true);
return item;
}
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;
@@ -98,6 +216,15 @@ public:
typedef std::reverse_iterator<ValueIterator> ReverseValueIterator;
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.
/// If not set by setItemDisplayer(), menu won't display anything.
/// @see setItemDisplayer()
@@ -120,21 +247,14 @@ public:
/// @param size requested size
void resizeList(size_t new_size);
/// Adds new option to list
/// @param item object that has to be added
/// @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 a new option to list
void addItem(ItemT item, Properties::Type properties = Properties::Selectable);
/// Adds separator to list
void addSeparator();
/// Inserts new option to list at given position
/// @param pos initial position of inserted item
/// @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 a new option to the list at given position
void insertItem(size_t pos, ItemT item, Properties::Type properties = Properties::Selectable);
/// Inserts separator to list at given position
/// @param pos initial position of inserted separator
@@ -149,12 +269,19 @@ public:
/// @return true if the position is reachable, false otherwise
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
/// @param pos position to be highlighted
void highlight(size_t pos);
/// @return currently highlighted position
size_t choice() const;
virtual void highlight(size_t position) OVERRIDE;
/// Refreshes the menu window
/// @see Window::refresh()
@@ -201,13 +328,6 @@ public:
/// @param state state of centered cursor
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
/// drawing function that is called by refresh()
/// @see refresh()
@@ -261,8 +381,26 @@ public:
ReverseValueIterator rendV() { return ReverseValueIterator(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)
{
return !m_items[pos].isSeparator()

View File

@@ -114,9 +114,9 @@ void Menu<ItemT>::resizeList(size_t new_size)
}
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>
@@ -126,9 +126,9 @@ void Menu<ItemT>::addSeparator()
}
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>
@@ -181,9 +181,9 @@ void Menu<ItemT>::refresh()
}
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;
for (; m_drawn_position < end; ++m_drawn_position, ++line)
for (; m_drawn_position < end_; ++m_drawn_position, ++line)
{
goToXY(0, line);
if (m_drawn_position >= m_items.size())

View File

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

View File

@@ -31,7 +31,9 @@
#include "song.h"
#include "status.h"
#include "statusbar.h"
#include "helpers/song_iterator_maker.h"
#include "utility/comparators.h"
#include "utility/functional.h"
#include "title.h"
using Global::MainHeight;
@@ -63,12 +65,12 @@ Playlist::Playlist()
{
case DisplayMode::Classic:
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;
case DisplayMode::Columns:
w.setItemDisplayer(std::bind(
Display::SongsInColumns, ph::_1, proxySongList()
Display::SongsInColumns, ph::_1, std::cref(w)
));
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> result;
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;
return w.getSelectedSongs();
}
/***********************************************************************/

View File

@@ -28,12 +28,13 @@
#include "regex_filter.h"
#include "screen.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();
// Screen<NC::Menu<MPD::Song>> implementation
// Screen<SongMenu> implementation
virtual void switchTo() 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;
// 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;
// private members

View File

@@ -34,6 +34,8 @@
#include "status.h"
#include "statusbar.h"
#include "tag_editor.h"
#include "helpers/song_iterator_maker.h"
#include "utility/functional.h"
#include "utility/comparators.h"
#include "title.h"
#include "screen_switcher.h"
@@ -86,14 +88,14 @@ PlaylistEditor::PlaylistEditor()
switch (Config.playlist_editor_display_mode)
{
case DisplayMode::Classic:
Content.setItemDisplayer(
std::bind(Display::Songs, ph::_1, contentProxyList(), std::cref(Config.song_list_format)
Content.setItemDisplayer(std::bind(
Display::Songs, ph::_1, std::cref(Content), std::cref(Config.song_list_format)
));
break;
case DisplayMode::Columns:
Content.setItemDisplayer(
std::bind(Display::SongsInColumns, ph::_1, contentProxyList())
);
Content.setItemDisplayer(std::bind(
Display::SongsInColumns, ph::_1, std::cref(Content)
));
break;
}
@@ -134,7 +136,7 @@ void PlaylistEditor::refresh()
void PlaylistEditor::switchTo()
{
SwitchTo::execute(this);
markSongsInPlaylist(contentProxyList());
markSongsInPlaylist(Content);
drawHeader();
refresh();
}
@@ -171,14 +173,19 @@ void PlaylistEditor::update()
MPD::SongIterator s = Mpd.GetPlaylistContent(Playlists.current()->value().path()), end;
for (; s != end; ++s, ++idx)
{
bool is_bold = myPlaylist->checkForSong(*s);
bool in_playlist = myPlaylist->checkForSong(*s);
if (idx < Content.size())
{
Content[idx].setBold(is_bold);
Content[idx].setBold(in_playlist);
Content[idx].value() = std::move(*s);
}
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())
Content.resizeList(idx);
@@ -218,13 +225,6 @@ int PlaylistEditor::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)
{
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> result;
@@ -412,14 +382,7 @@ std::vector<MPD::Song> PlaylistEditor::getSelectedSongs()
}
}
else if (isActiveWindow(Content))
{
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());
}
result = Content.getSelectedSongs();
return result;
}

View File

@@ -26,6 +26,7 @@
#include "interfaces.h"
#include "regex_filter.h"
#include "screen.h"
#include "song_list.h"
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;
// 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;
// HasColumns implementation
@@ -77,10 +73,9 @@ struct PlaylistEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, T
void requestContentsUpdate() { m_content_update_requested = true; }
virtual void Locate(const MPD::Playlist &playlist);
ProxySongList contentProxyList();
NC::Menu<MPD::Playlist> Playlists;
NC::Menu<MPD::Song> Content;
SongMenu Content;
private:
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() { }
/// @see Screen::isActiveWindow()
virtual bool isActiveWindow(const NC::Window &w_) = 0;
virtual bool isActiveWindow(const NC::Window &w_) const = 0;
/// @see Screen::activeWindow()
virtual void *activeWindow() = 0;
virtual NC::Window *activeWindow() = 0;
virtual const NC::Window *activeWindow() const = 0;
/// @see Screen::refresh()
virtual void refresh() = 0;
@@ -123,19 +124,26 @@ template <typename WindowT> struct Screen : public BaseScreen
{
typedef WindowT WindowType;
typedef typename std::add_lvalue_reference<WindowType>::type WindowReference;
typedef typename std::add_lvalue_reference<
typename std::add_const<WindowType>::type
>::type ConstWindowReference;
private:
template <bool IsPointer, typename Result> struct getObject { };
template <typename Result> struct getObject<true, Result> {
static Result apply(WindowType w) { return *w; }
template <bool IsPointer, typename Result, typename ConstResult>
struct getObject {
static Result &apply(WindowReference w) { return w; }
static ConstResult &constApply(ConstWindowReference w) { return w; }
};
template <typename Result> struct getObject<false, Result> {
static Result apply(WindowReference w) { return w; }
template <typename Result, typename ConstResult>
struct getObject<true, Result, ConstResult> {
static Result &apply(WindowType w) { return *w; }
static ConstResult &constApply(const WindowType w) { return *w; }
};
typedef getObject<
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
>::type
> Accessor;
@@ -146,17 +154,20 @@ public:
virtual ~Screen() { }
virtual bool isActiveWindow(const NC::Window &w_) OVERRIDE {
return &Accessor::apply(w) == &w_;
virtual bool isActiveWindow(const NC::Window &w_) const OVERRIDE {
return &Accessor::constApply(w) == &w_;
}
/// Since some screens contain more that one window
/// it's useful to determine the one that is being
/// active
/// @return address to window object cast to void *
virtual void *activeWindow() OVERRIDE {
virtual NC::Window *activeWindow() OVERRIDE {
return &Accessor::apply(w);
}
virtual const NC::Window *activeWindow() const OVERRIDE {
return &Accessor::constApply(w);
}
/// Refreshes whole screen
virtual void refresh() OVERRIDE {

View File

@@ -31,6 +31,7 @@
#include "settings.h"
#include "status.h"
#include "statusbar.h"
#include "helpers/song_iterator_maker.h"
#include "utility/comparators.h"
#include "title.h"
#include "screen_switcher.h"
@@ -74,8 +75,63 @@ namespace pos {
std::string SEItemToString(const SEItem &ei);
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[] =
{
"Any",
@@ -109,7 +165,7 @@ SearchEngine::SearchEngine()
w.setHighlightColor(Config.main_highlight_color);
w.cyclicScrolling(Config.use_cyclic_scrolling);
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.setSelectedSuffix(Config.selected_item_suffix);
SearchMode = &SearchModes[Config.search_engine_default_search_mode];
@@ -140,7 +196,7 @@ void SearchEngine::switchTo()
SwitchTo::execute(this);
if (w.empty())
Prepare();
markSongsInPlaylist(proxySongList());
markSongsInPlaylist(w);
drawHeader();
}
@@ -190,10 +246,10 @@ void SearchEngine::enterPressed()
size_t found = w.size()-SearchEngine::StaticOptions;
found += 3; // don't count options inserted below
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.insertSeparator(ResetButton+3);
markSongsInPlaylist(proxySongList());
markSongsInPlaylist(w);
Statusbar::print("Searching finished");
if (Config.block_search_constraints_change)
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> result;
@@ -328,6 +359,9 @@ void SearchEngine::Prepare()
w.setTitle("");
w.clear();
w.resizeList(StaticOptions-3);
for (auto &item : w)
item.setSelectable(false);
w.at(ConstraintsNumber).setSeparator(true);
w.at(SearchButton-1).setSeparator(true);

View File

@@ -27,6 +27,7 @@
#include "mpdpp.h"
#include "regex_filter.h"
#include "screen.h"
#include "song_list.h"
struct SEItem
{
@@ -74,11 +75,27 @@ private:
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();
// Screen< NC::Menu<SEItem> > implementation
// Screen<SearchEngineWindow> implementation
virtual void resize() 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;
// 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;
// private members

View File

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

View File

@@ -93,7 +93,7 @@ void SongInfo::switchTo()
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 << "Directory: " << NC::Format::NoBold << Config.color2;

View File

@@ -55,7 +55,7 @@ struct SongInfo: Screen<NC::Scrollpad>, Tabbable
static const Metadata Tags[];
private:
void PrepareSong(MPD::Song &);
void PrepareSong(const MPD::Song &s);
};
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();
if (isVisible(myBrowser))
markSongsInPlaylist(myBrowser->proxySongList());
markSongsInPlaylist(myBrowser->main());
if (isVisible(mySearcher))
markSongsInPlaylist(mySearcher->proxySongList());
markSongsInPlaylist(mySearcher->main());
if (isVisible(myLibrary))
{
markSongsInPlaylist(myLibrary->songsProxyList());
markSongsInPlaylist(myLibrary->Songs);
myLibrary->Songs.refresh();
}
if (isVisible(myPlaylistEditor))
{
markSongsInPlaylist(myPlaylistEditor->contentProxyList());
markSongsInPlaylist(myPlaylistEditor->Content);
myPlaylistEditor->Content.refresh();
}
}

View File

@@ -36,6 +36,8 @@
#include "playlist.h"
#include "song_info.h"
#include "statusbar.h"
#include "helpers/song_iterator_maker.h"
#include "utility/functional.h"
#include "utility/comparators.h"
#include "title.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 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("/")
{
PatternsFile = Config.ncmpcpp_directory + "patterns.list";
@@ -115,7 +169,7 @@ TagEditor::TagEditor() : FParser(0), FParserHelper(0), FParserLegend(0), FParser
TagTypes->addSeparator();
if (Config.titles_visibility)
{
TagTypes->addItem("Options", 1, 1);
TagTypes->addItem("Options", NC::List::Properties::Bold | NC::List::Properties::Inactive);
TagTypes->addSeparator();
}
TagTypes->addItem("Capitalize First Letters");
@@ -124,7 +178,7 @@ TagEditor::TagEditor() : FParser(0), FParserHelper(0), FParserLegend(0), FParser
TagTypes->addItem("Reset");
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->cyclicScrolling(Config.use_cyclic_scrolling);
Tags->centeredCursor(Config.centered_cursor);
@@ -337,7 +391,7 @@ void TagEditor::enterPressed()
if (!Patterns.empty())
{
FParser->addSeparator();
FParser->addItem("Recent patterns", 1, 1);
FParser->addItem("Recent patterns", NC::List::Properties::Bold | NC::List::Properties::Inactive);
FParser->addSeparator();
for (std::list<std::string>::const_iterator it = Patterns.begin(); it != Patterns.end(); ++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> result;

View File

@@ -31,6 +31,23 @@
#include "mutable_song.h"
#include "regex_filter.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
{
@@ -59,11 +76,6 @@ struct TagEditor: Screen<NC::Window *>, HasColumns, HasSongs, Searchable, Tabbab
virtual bool find(SearchDirection direction, bool wrap, bool skip_current) OVERRIDE;
// 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;
// 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::string> *TagTypes;
NC::Menu<MPD::MutableSong> *Tags;
TagsWindow *Tags;
private:
void SetDimensions(size_t, size_t);

View File

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