song list: get rid of boost::zip_iterator and improve {Const,}SongIterator

This commit is contained in:
Andrzej Rybczak
2016-12-08 04:28:43 +01:00
parent 612f8c3145
commit b7386c4fa6
15 changed files with 244 additions and 161 deletions

View File

@@ -59,6 +59,7 @@ ncmpcpp_LDFLAGS = $(all_libraries)
noinst_HEADERS = \
helpers/song_iterator_maker.h \
utility/comparators.h \
utility/const.h \
utility/conversion.h \
utility/functional.h \
utility/html.h \

View File

@@ -80,9 +80,9 @@ std::vector<std::shared_ptr<Actions::BaseAction>> AvailableActions;
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);
bool scrollTagCanBeRun(NC::List *&list, const SongList *&songs);
void scrollTagUpRun(NC::List *list, const SongList *songs, MPD::Song::GetFunction get);
void scrollTagDownRun(NC::List *list, const SongList *songs, MPD::Song::GetFunction get);
void seek();
void findItem(const SearchDirection direction);
@@ -1783,28 +1783,25 @@ bool SelectAlbum::canBeRun()
void SelectAlbum::run()
{
const auto front = m_songs->beginS(), current = m_songs->currentS(), end = m_songs->endS();
auto *s = current->get<Bit::Song>();
if (s == nullptr)
if (current->song() == nullptr)
return;
auto get = &MPD::Song::getAlbum;
const std::string tag = s->getTags(get);
const std::string tag = current->song()->getTags(get);
// go up
for (auto it = current; it != front;)
{
--it;
s = it->get<Bit::Song>();
if (s == nullptr || s->getTags(get) != tag)
if (it->song() == nullptr || it->song()->getTags(get) != tag)
break;
it->get<Bit::Properties>().setSelected(true);
it->properties().setSelected(true);
}
// go down
for (auto it = current;;)
{
it->get<Bit::Properties>().setSelected(true);
it->properties().setSelected(true);
if (++it == end)
break;
s = it->get<Bit::Song>();
if (s == nullptr || s->getTags(get) != tag)
if (it->song() == nullptr || it->song()->getTags(get) != tag)
break;
}
Statusbar::print("Album around cursor position selected");
@@ -2809,7 +2806,7 @@ void populateActions()
}
}
bool scrollTagCanBeRun(NC::List *&list, SongList *&songs)
bool scrollTagCanBeRun(NC::List *&list, const SongList *&songs)
{
auto w = myScreen->activeWindow();
if (list != static_cast<void *>(w))
@@ -2820,36 +2817,34 @@ bool scrollTagCanBeRun(NC::List *&list, SongList *&songs)
&& songs != nullptr;
}
void scrollTagUpRun(NC::List *list, SongList *songs, MPD::Song::GetFunction get)
void scrollTagUpRun(NC::List *list, const SongList *songs, MPD::Song::GetFunction get)
{
const auto front = songs->beginS();
auto it = songs->currentS();
if (auto *s = it->get<Bit::Song>())
if (it->song() != nullptr)
{
const std::string tag = s->getTags(get);
const std::string tag = it->song()->getTags(get);
while (it != front)
{
--it;
s = it->get<Bit::Song>();
if (s == nullptr || s->getTags(get) != tag)
if (it->song() == nullptr || it->song()->getTags(get) != tag)
break;
}
list->highlight(it-front);
}
}
void scrollTagDownRun(NC::List *list, SongList *songs, MPD::Song::GetFunction get)
void scrollTagDownRun(NC::List *list, const 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>())
if (it->song() != nullptr)
{
const std::string tag = s->getTags(get);
const std::string tag = it->song()->getTags(get);
while (it != back)
{
++it;
s = it->get<Bit::Song>();
if (s == nullptr || s->getTags(get) != tag)
if (it->song() == nullptr || it->song()->getTags(get) != tag)
break;
}
list->highlight(it-front);
@@ -2882,7 +2877,8 @@ void seek()
// can be run and one of them is of the given type. This will still not work
// in some contrived cases, but allows for more flexibility than accepting
// single actions only.
auto hasRunnableAction = [](BindingsConfiguration::BindingIteratorPair &bindings, Actions::Type type) {
auto hasRunnableAction = [](BindingsConfiguration::BindingIteratorPair &bindings,
Actions::Type type) {
bool success = false;
for (auto binding = bindings.first; binding != bindings.second; ++binding)
{

View File

@@ -280,7 +280,7 @@ private:
virtual void run() override;
NC::List *m_list;
SongList *m_songs;
const SongList *m_songs;
};
struct ScrollUpAlbum: BaseAction
@@ -292,7 +292,7 @@ private:
virtual void run() override;
NC::List *m_list;
SongList *m_songs;
const SongList *m_songs;
};
struct ScrollDownArtist: BaseAction
@@ -304,7 +304,7 @@ private:
virtual void run() override;
NC::List *m_list;
SongList *m_songs;
const SongList *m_songs;
};
struct ScrollDownAlbum: BaseAction
@@ -316,7 +316,7 @@ private:
virtual void run() override;
NC::List *m_list;
SongList *m_songs;
const SongList *m_songs;
};
struct PageUp: BaseAction

View File

@@ -70,54 +70,53 @@ 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;
}
};
}
template <>
struct SongPropertiesExtractor<MPD::Item>
{
template <typename ItemT>
auto &operator()(ItemT &item) const
{
auto s = item.value().type() == MPD::Item::Type::Song
? &item.value().song()
: nullptr;
m_cache.assign(&item.properties(), s);
return m_cache;
}
private:
mutable SongProperties m_cache;
};
SongIterator BrowserWindow::currentS()
{
return makeSongIterator_<MPD::Item>(current(), SongExtractor<false>());
return makeSongIterator(current());
}
ConstSongIterator BrowserWindow::currentS() const
{
return makeConstSongIterator_<MPD::Item>(current(), SongExtractor<true>());
return makeConstSongIterator(current());
}
SongIterator BrowserWindow::beginS()
{
return makeSongIterator_<MPD::Item>(begin(), SongExtractor<false>());
return makeSongIterator(begin());
}
ConstSongIterator BrowserWindow::beginS() const
{
return makeConstSongIterator_<MPD::Item>(begin(), SongExtractor<true>());
return makeConstSongIterator(begin());
}
SongIterator BrowserWindow::endS()
{
return makeSongIterator_<MPD::Item>(end(), SongExtractor<false>());
return makeSongIterator(end());
}
ConstSongIterator BrowserWindow::endS() const
{
return makeConstSongIterator_<MPD::Item>(end(), SongExtractor<true>());
return makeConstSongIterator(end());
}
std::vector<MPD::Song> BrowserWindow::getSelectedSongs()

View File

@@ -87,8 +87,7 @@ void setProperties(NC::Menu<T> &menu, const MPD::Song &s, const SongList &list,
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())
if (next->song() != nullptr && next->song()->getAlbum() != s.getAlbum())
separate_albums = true;
}
}

View File

@@ -35,7 +35,7 @@ const MPD::Song *currentSong(const BaseScreen *screen)
{
const auto it = list->currentS();
if (it != list->endS())
ptr = it->get<Bit::Song>();
ptr = it->song();
}
return ptr;
}

View File

@@ -387,12 +387,10 @@ template <typename ListT>
void markSongsInPlaylist(ListT &list)
{
ScopedUnfilteredMenu<typename ListT::Item::Type> sunfilter(ReapplyFilter::No, list);
MPD::Song *s;
for (auto &p : static_cast<SongList &>(list))
{
s = p.get<Bit::Song>();
if (s != nullptr)
p.get<Bit::Properties>().setBold(myPlaylist->checkForSong(*s));
if (p.song() != nullptr)
p.properties().setBold(myPlaylist->checkForSong(*p.song()));
}
}

View File

@@ -22,26 +22,48 @@
#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)
template <typename SongT>
struct SongPropertiesExtractor
{
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>
auto &operator()(ItemT &item) const
{
return m_cache.assign(&item.properties(), &item.value());
}
private:
mutable SongProperties m_cache;
};
template <typename IteratorT>
SongIterator makeSongIterator(IteratorT it)
{
typedef SongPropertiesExtractor<
typename IteratorT::value_type::Type
> Extractor;
static_assert(
std::is_convertible<
typename std::result_of<Extractor(typename IteratorT::reference)>::type,
SongProperties &
>::value, "invalid result type of SongPropertiesExtractor");
return SongIterator(boost::make_transform_iterator(it, Extractor{}));
}
template <typename ItemT, typename TransformT>
ConstSongIterator makeConstSongIterator_(typename NC::Menu<ItemT>::ConstIterator it, TransformT &&map)
template <typename ConstIteratorT>
ConstSongIterator makeConstSongIterator(ConstIteratorT it)
{
return ConstSongIterator(boost::make_zip_iterator(boost::make_tuple(
typename NC::Menu<ItemT>::ConstPropertiesIterator(it),
boost::make_transform_iterator(it, std::forward<TransformT>(map))
)));
typedef SongPropertiesExtractor<
typename ConstIteratorT::value_type::Type
> Extractor;
static_assert(
std::is_convertible<
typename std::result_of<Extractor(typename ConstIteratorT::reference)>::type,
const SongProperties &
>::value, "invalid result type of SongPropertiesExtractor");
return ConstSongIterator(boost::make_transform_iterator(it, Extractor{}));
}
#endif // NCMPCPP_HELPERS_SONG_ITERATOR_MAKER_H

View File

@@ -29,6 +29,7 @@
#include <memory>
#include <set>
#include "utility/const.h"
#include "strbuffer.h"
#include "window.h"
@@ -197,8 +198,6 @@ struct Menu: Window, List
}
private:
enum class Const { Yes, No };
template <Const const_>
struct ExtractProperties
{
@@ -254,19 +253,19 @@ struct Menu: Window, List
typedef std::reverse_iterator<ConstIterator> ConstReverseIterator;
typedef boost::transform_iterator<
typename Item::template ExtractValue<Item::Const::No>,
typename Item::template ExtractValue<Const::No>,
Iterator> ValueIterator;
typedef boost::transform_iterator<
typename Item::template ExtractValue<Item::Const::Yes>,
typename Item::template ExtractValue<Const::Yes>,
ConstIterator> ConstValueIterator;
typedef std::reverse_iterator<ValueIterator> ReverseValueIterator;
typedef std::reverse_iterator<ConstValueIterator> ConstReverseValueIterator;
typedef boost::transform_iterator<
typename Item::template ExtractProperties<Item::Const::No>,
typename Item::template ExtractProperties<Const::No>,
Iterator> PropertiesIterator;
typedef boost::transform_iterator<
typename Item::template ExtractProperties<Item::Const::Yes>,
typename Item::template ExtractProperties<Const::Yes>,
ConstIterator> ConstPropertiesIterator;
/// Function helper prototype used to display each option on the screen.

View File

@@ -260,6 +260,23 @@ struct Item
{
return m_type;
}
Directory &directory()
{
return const_cast<Directory &>(
static_cast<const Item &>(*this).directory());
}
Song &song()
{
return const_cast<Song &>(
static_cast<const Item &>(*this).song());
}
Playlist &playlist()
{
return const_cast<Playlist &>(
static_cast<const Item &>(*this).playlist());
}
const Directory &directory() const
{
assert(m_type == Type::Directory);

View File

@@ -73,56 +73,56 @@ 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;
}
};
bool SEItemEntryMatcher(const Regex::Regex &rx,
const NC::Menu<SEItem>::Item &item,
bool filter);
}
template <>
struct SongPropertiesExtractor<SEItem>
{
template <typename ItemT>
auto &operator()(ItemT &item) const
{
auto s = !item.isSeparator() && item.value().isSong()
? &item.value().song()
: nullptr;
return m_cache.assign(&item.properties(), s);
}
private:
mutable SongProperties m_cache;
};
SongIterator SearchEngineWindow::currentS()
{
return makeSongIterator_<SEItem>(current(), SongExtractor<false>());
return makeSongIterator(current());
}
ConstSongIterator SearchEngineWindow::currentS() const
{
return makeConstSongIterator_<SEItem>(current(), SongExtractor<true>());
return makeConstSongIterator(current());
}
SongIterator SearchEngineWindow::beginS()
{
return makeSongIterator_<SEItem>(begin(), SongExtractor<false>());
return makeSongIterator(begin());
}
ConstSongIterator SearchEngineWindow::beginS() const
{
return makeConstSongIterator_<SEItem>(begin(), SongExtractor<true>());
return makeConstSongIterator(begin());
}
SongIterator SearchEngineWindow::endS()
{
return makeSongIterator_<SEItem>(end(), SongExtractor<false>());
return makeSongIterator(end());
}
ConstSongIterator SearchEngineWindow::endS() const
{
return makeConstSongIterator_<SEItem>(end(), SongExtractor<true>());
return makeConstSongIterator(end());
}
std::vector<MPD::Song> SearchEngineWindow::getSelectedSongs()

View File

@@ -22,53 +22,34 @@
#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>());
return makeSongIterator(current());
}
ConstSongIterator SongMenu::currentS() const
{
return makeConstSongIterator_<MPD::Song>(current(), SongExtractor<true>());
return makeConstSongIterator(current());
}
SongIterator SongMenu::beginS()
{
return makeSongIterator_<MPD::Song>(begin(), SongExtractor<false>());
return makeSongIterator(begin());
}
ConstSongIterator SongMenu::beginS() const
{
return makeConstSongIterator_<MPD::Song>(begin(), SongExtractor<true>());
return makeConstSongIterator(begin());
}
SongIterator SongMenu::endS()
{
return makeSongIterator_<MPD::Song>(end(), SongExtractor<false>());
return makeSongIterator(end());
}
ConstSongIterator SongMenu::endS() const
{
return makeConstSongIterator_<MPD::Song>(end(), SongExtractor<true>());
return makeConstSongIterator(end());
}
std::vector<MPD::Song> SongMenu::getSelectedSongs()

View File

@@ -22,25 +22,85 @@
#define NCMPCPP_SONG_LIST_H
#include <boost/range/detail/any_iterator.hpp>
#include <boost/tuple/tuple.hpp>
#include "menu.h"
#include "song.h"
#include "utility/const.h"
template <typename ValueT>
struct SongProperties
{
enum class State { Undefined, Const, Mutable };
SongProperties()
: m_state(State::Undefined)
{ }
SongProperties &assign(NC::List::Properties *properties_, MPD::Song *song_)
{
m_state = State::Mutable;
m_properties = properties_;
m_song = song_;
return *this;
}
SongProperties &assign(const NC::List::Properties *properties_, const MPD::Song *song_)
{
m_state = State::Const;
m_const_properties = properties_;
m_const_song = song_;
return *this;
}
const NC::List::Properties &properties() const
{
assert(m_state != State::Undefined);
return *m_const_properties;
}
const MPD::Song *song() const
{
assert(m_state != State::Undefined);
return m_const_song;
}
NC::List::Properties &properties()
{
assert(m_state == State::Mutable);
return *m_properties;
}
MPD::Song *song()
{
assert(m_state == State::Mutable);
return m_song;
}
private:
State m_state;
union {
NC::List::Properties *m_properties;
const NC::List::Properties *m_const_properties;
};
union {
MPD::Song *m_song;
const MPD::Song *m_const_song;
};
};
template <Const const_>
using SongIteratorT = boost::range_detail::any_iterator<
ValueT,
typename std::conditional<
const_ == Const::Yes,
const SongProperties,
SongProperties>::type,
boost::random_access_traversal_tag,
const ValueT, // const needed, see https://svn.boost.org/trac/boost/ticket/10493
typename std::conditional<
const_ == Const::Yes,
const SongProperties &,
SongProperties &>::type,
std::ptrdiff_t
>;
>;
typedef SongIteratorT<boost::tuple<NC::List::Properties &, MPD::Song *>> SongIterator;
typedef SongIteratorT<boost::tuple<const NC::List::Properties &, const MPD::Song *>> ConstSongIterator;
namespace Bit {
const size_t Properties = 0;
const size_t Song = 1;
}
typedef SongIteratorT<Const::No> SongIterator;
typedef SongIteratorT<Const::Yes> ConstSongIterator;
struct SongList
{

View File

@@ -87,51 +87,36 @@ 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>());
return makeSongIterator(current());
}
ConstSongIterator TagsWindow::currentS() const
{
return makeConstSongIterator_<MPD::MutableSong>(current(), SongExtractor<true>());
return makeConstSongIterator(current());
}
SongIterator TagsWindow::beginS()
{
return makeSongIterator_<MPD::MutableSong>(begin(), SongExtractor<false>());
return makeSongIterator(begin());
}
ConstSongIterator TagsWindow::beginS() const
{
return makeConstSongIterator_<MPD::MutableSong>(begin(), SongExtractor<true>());
return makeConstSongIterator(begin());
}
SongIterator TagsWindow::endS()
{
return makeSongIterator_<MPD::MutableSong>(end(), SongExtractor<false>());
return makeSongIterator(end());
}
ConstSongIterator TagsWindow::endS() const
{
return makeConstSongIterator_<MPD::MutableSong>(end(), SongExtractor<true>());
return makeConstSongIterator(end());
}
std::vector<MPD::Song> TagsWindow::getSelectedSongs()

26
src/utility/const.h Normal file
View File

@@ -0,0 +1,26 @@
/***************************************************************************
* Copyright (C) 2008-2016 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_UTILITY_CONST_H
#define NCMPCPP_UTILITY_CONST_H
enum class Const { Yes, No };
#endif // NCMPCPP_UTILITY_CONST_H