Simplify option_parser
This commit is contained in:
@@ -390,6 +390,8 @@
|
|||||||
##
|
##
|
||||||
#lastfm_preferred_language = en
|
#lastfm_preferred_language = en
|
||||||
#
|
#
|
||||||
|
#space_add_mode = add_remove
|
||||||
|
#
|
||||||
#show_hidden_files_in_local_browser = no
|
#show_hidden_files_in_local_browser = no
|
||||||
#
|
#
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ If enabled, each time song changes lyrics fetcher will be automatically run in b
|
|||||||
If enabled, lyrics will be saved in song's directory, otherwise in ~/.lyrics. Note that it needs properly set mpd_music_dir.
|
If enabled, lyrics will be saved in song's directory, otherwise in ~/.lyrics. Note that it needs properly set mpd_music_dir.
|
||||||
.TP
|
.TP
|
||||||
.B generate_win32_compatible_filenames = yes/no
|
.B generate_win32_compatible_filenames = yes/no
|
||||||
If set to yes, filenames generated by ncmpcpp (with tag editor, for lyrics, artists etc.) will not contain the following characters: \\?*:|"<> - otherwise only slash (/) will not be used.
|
If set to yes, filenames generated by ncmpcpp (with tag editor, for lyrics, artists etc.) will not contain the following characters: \\?*:|\"<> - otherwise only slash (/) will not be used.
|
||||||
.TP
|
.TP
|
||||||
.B allow_for_physical_item_deletion = yes/no
|
.B allow_for_physical_item_deletion = yes/no
|
||||||
If set to yes, it will be possible to physically delete files and directories from the disk in the browser.
|
If set to yes, it will be possible to physically delete files and directories from the disk in the browser.
|
||||||
@@ -265,6 +265,9 @@ If set to yes, it will be possible to physically delete files and directories fr
|
|||||||
.B lastfm_preferred_language = ISO 639 alpha-2 language code
|
.B lastfm_preferred_language = ISO 639 alpha-2 language code
|
||||||
If set, ncmpcpp will try to get info from last.fm in language you set and if it fails, it will fall back to english. Otherwise it will use english the first time.
|
If set, ncmpcpp will try to get info from last.fm in language you set and if it fails, it will fall back to english. Otherwise it will use english the first time.
|
||||||
.TP
|
.TP
|
||||||
|
.B space_add_mode = add_remove/always_add
|
||||||
|
If set to add_remove, attepting to add files that are already in playlist will remove them. Otherwise they can be added multiple times.
|
||||||
|
.TP
|
||||||
.B show_hidden_files_in_local_browser = yes/no
|
.B show_hidden_files_in_local_browser = yes/no
|
||||||
Trigger for displaying in local browser files and directories that begin with '.'
|
Trigger for displaying in local browser files and directories that begin with '.'
|
||||||
.TP
|
.TP
|
||||||
|
|||||||
@@ -36,27 +36,31 @@
|
|||||||
#include "utility/html.h"
|
#include "utility/html.h"
|
||||||
#include "utility/string.h"
|
#include "utility/string.h"
|
||||||
|
|
||||||
std::unique_ptr<LyricsFetcher> toLyricsFetcher(const std::string &s)
|
std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher)
|
||||||
{
|
{
|
||||||
|
std::string s;
|
||||||
|
is >> s;
|
||||||
if (s == "lyricwiki")
|
if (s == "lyricwiki")
|
||||||
return std::make_unique<LyricwikiFetcher>();
|
fetcher = std::make_unique<LyricwikiFetcher>();
|
||||||
else if (s == "azlyrics")
|
else if (s == "azlyrics")
|
||||||
return std::make_unique<AzLyricsFetcher>();
|
fetcher = std::make_unique<AzLyricsFetcher>();
|
||||||
else if (s == "genius")
|
else if (s == "genius")
|
||||||
return std::make_unique<GeniusFetcher>();
|
fetcher = std::make_unique<GeniusFetcher>();
|
||||||
else if (s == "sing365")
|
else if (s == "sing365")
|
||||||
return std::make_unique<Sing365Fetcher>();
|
fetcher = std::make_unique<Sing365Fetcher>();
|
||||||
else if (s == "lyricsmania")
|
else if (s == "lyricsmania")
|
||||||
return std::make_unique<LyricsmaniaFetcher>();
|
fetcher = std::make_unique<LyricsmaniaFetcher>();
|
||||||
else if (s == "metrolyrics")
|
else if (s == "metrolyrics")
|
||||||
return std::make_unique<MetrolyricsFetcher>();
|
fetcher = std::make_unique<MetrolyricsFetcher>();
|
||||||
else if (s == "justsomelyrics")
|
else if (s == "justsomelyrics")
|
||||||
return std::make_unique<JustSomeLyricsFetcher>();
|
fetcher = std::make_unique<JustSomeLyricsFetcher>();
|
||||||
else if (s == "tekstowo")
|
else if (s == "tekstowo")
|
||||||
return std::make_unique<TekstowoFetcher>();
|
fetcher = std::make_unique<TekstowoFetcher>();
|
||||||
else if (s == "internet")
|
else if (s == "internet")
|
||||||
return std::make_unique<InternetLyricsFetcher>();
|
fetcher = std::make_unique<InternetLyricsFetcher>();
|
||||||
throw std::runtime_error("no lyrics fetcher named '" + s + "'");
|
else
|
||||||
|
is.setstate(std::ios::failbit);
|
||||||
|
return is;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char LyricsFetcher::msgNotFound[] = "Not found";
|
const char LyricsFetcher::msgNotFound[] = "Not found";
|
||||||
|
|||||||
@@ -47,9 +47,11 @@ protected:
|
|||||||
static const char msgNotFound[];
|
static const char msgNotFound[];
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<std::unique_ptr<LyricsFetcher>> LyricsFetchers;
|
typedef std::unique_ptr<LyricsFetcher> LyricsFetcher_;
|
||||||
|
|
||||||
std::unique_ptr<LyricsFetcher> toLyricsFetcher(const std::string &s);
|
typedef std::vector<LyricsFetcher_> LyricsFetchers;
|
||||||
|
|
||||||
|
std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher);
|
||||||
|
|
||||||
/**********************************************************************/
|
/**********************************************************************/
|
||||||
|
|
||||||
|
|||||||
848
src/settings.cpp
848
src/settings.cpp
@@ -19,6 +19,7 @@
|
|||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
#include <boost/tuple/tuple.hpp>
|
#include <boost/tuple/tuple.hpp>
|
||||||
|
#include <boost/tokenizer.hpp>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
@@ -166,567 +167,346 @@ std::string adjust_path(std::string s)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parser worker for buffer
|
NC::Buffer buffer(const std::string &v)
|
||||||
template <typename ValueT, typename TransformT>
|
|
||||||
option_parser::worker buffer(NC::Buffer &arg, ValueT &&value, TransformT &&map)
|
|
||||||
{
|
{
|
||||||
return option_parser::worker(assign<std::string>(arg, [&arg, map](std::string s) {
|
NC::Buffer result;
|
||||||
NC::Buffer result;
|
Format::print(
|
||||||
auto ast = Format::parse(s, Format::Flags::Color | Format::Flags::Format);
|
Format::parse(v, Format::Flags::Color | Format::Flags::Format),
|
||||||
Format::print(ast, result, nullptr);
|
result,
|
||||||
return map(std::move(result));
|
nullptr);
|
||||||
}), defaults_to(arg, map(std::forward<ValueT>(value))));
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
option_parser::worker border(NC::Border &arg, NC::Border value)
|
void deprecated(const char *option, double version_removal)
|
||||||
{
|
{
|
||||||
return option_parser::worker(assign<std::string>(arg, [&arg](std::string s) {
|
std::cerr << "WARNING: " << option
|
||||||
NC::Border result;
|
<< " is deprecated and will be removed in " << version_removal << "\n";
|
||||||
if (!s.empty())
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
result = boost::lexical_cast<NC::Color>(s);
|
|
||||||
} catch (boost::bad_lexical_cast &) {
|
|
||||||
throw std::runtime_error("invalid border: " + s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}), defaults_to(arg, std::move(value)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
option_parser::worker deprecated(const char *option, double version_removal)
|
|
||||||
{
|
|
||||||
return option_parser::worker([option, version_removal](std::string) {
|
|
||||||
std::cerr << "WARNING: " << option << " is deprecated and will be removed in " << version_removal << ".\n";
|
|
||||||
}, [] { }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Configuration::read(const std::vector<std::string> &config_paths, bool ignore_errors)
|
bool Configuration::read(const std::vector<std::string> &config_paths, bool ignore_errors)
|
||||||
{
|
{
|
||||||
std::string mpd_host;
|
|
||||||
unsigned mpd_port;
|
|
||||||
std::string columns_format;
|
|
||||||
|
|
||||||
option_parser p;
|
option_parser p;
|
||||||
|
|
||||||
// deprecation warnings
|
// deprecation warnings
|
||||||
p.add("default_space_mode", deprecated("default_space_mode", 0.8));
|
p.add<void>("default_space_mode", nullptr, "", [](std::string) {
|
||||||
|
deprecated("default_space_mode", 0.8);
|
||||||
|
});
|
||||||
|
|
||||||
// keep the same order of variables as in configuration file
|
// keep the same order of variables as in configuration file
|
||||||
p.add("ncmpcpp_directory", assign_default<std::string>(
|
p.add("ncmpcpp_directory", &ncmpcpp_directory, "~/.ncmpcpp/", adjust_directory);
|
||||||
ncmpcpp_directory, "~/.ncmpcpp/", adjust_directory
|
p.add("lyrics_directory", &lyrics_directory, "~/.lyrics/", adjust_directory);
|
||||||
));
|
p.add<void>("mpd_host", nullptr, "localhost", [](std::string host) {
|
||||||
p.add("lyrics_directory", assign_default<std::string>(
|
|
||||||
lyrics_directory, "~/.lyrics/", adjust_directory
|
|
||||||
));
|
|
||||||
p.add("mpd_host", assign_default<std::string>(
|
|
||||||
mpd_host, "localhost", [](std::string host) {
|
|
||||||
// host can be a path to ipc socket, relative to home directory
|
|
||||||
expand_home(host);
|
expand_home(host);
|
||||||
Mpd.SetHostname(host);
|
Mpd.SetHostname(host);
|
||||||
return host;
|
});
|
||||||
}));
|
p.add<void>("mpd_port", nullptr, "6600", [](std::string port) {
|
||||||
p.add("mpd_port", assign_default<unsigned>(
|
Mpd.SetPort(verbose_lexical_cast<unsigned>(port));
|
||||||
mpd_port, 6600, [](unsigned port) {
|
});
|
||||||
Mpd.SetPort(port);
|
p.add("mpd_music_dir", &mpd_music_dir, "~/music", adjust_directory);
|
||||||
return port;
|
p.add("mpd_connection_timeout", &mpd_connection_timeout, "5");
|
||||||
}));
|
p.add("mpd_crossfade_time", &crossfade_time, "5");
|
||||||
p.add("mpd_music_dir", assign_default<std::string>(
|
p.add("visualizer_fifo_path", &visualizer_fifo_path, "/tmp/mpd.fifo", adjust_path);
|
||||||
mpd_music_dir, "~/music", adjust_directory
|
p.add("visualizer_output_name", &visualizer_output_name, "Visualizer feed");
|
||||||
));
|
p.add("visualizer_in_stereo", &visualizer_in_stereo, "yes", yes_no);
|
||||||
p.add("mpd_connection_timeout", assign_default(
|
p.add("visualizer_sample_multiplier", &visualizer_sample_multiplier, "1",
|
||||||
mpd_connection_timeout, 5
|
[](std::string v) {
|
||||||
));
|
double multiplier = verbose_lexical_cast<double>(v);
|
||||||
p.add("mpd_crossfade_time", assign_default(
|
lowerBoundCheck(multiplier, 0.0);
|
||||||
crossfade_time, 5
|
return multiplier;
|
||||||
));
|
});
|
||||||
p.add("visualizer_fifo_path", assign_default<std::string>(
|
p.add("visualizer_sync_interval", &visualizer_sync_interval, "30",
|
||||||
visualizer_fifo_path, "/tmp/mpd.fifo", adjust_path
|
[](std::string v) {
|
||||||
));
|
unsigned sync_interval = verbose_lexical_cast<unsigned>(v);
|
||||||
p.add("visualizer_output_name", assign_default(
|
lowerBoundCheck<unsigned>(sync_interval, 10);
|
||||||
visualizer_output_name, "Visualizer feed"
|
return boost::posix_time::seconds(sync_interval);
|
||||||
));
|
});
|
||||||
p.add("visualizer_in_stereo", yes_no(
|
p.add("visualizer_type", &visualizer_type, "wave");
|
||||||
visualizer_in_stereo, true
|
p.add("visualizer_look", &visualizer_chars, "●▮", [](std::string s) {
|
||||||
));
|
|
||||||
p.add("visualizer_sample_multiplier", assign_default<double>(
|
|
||||||
visualizer_sample_multiplier, 1.0, [](double v) {
|
|
||||||
lowerBoundCheck(v, 0.0);
|
|
||||||
return v;
|
|
||||||
}));
|
|
||||||
p.add("visualizer_sync_interval", assign_default<unsigned>(
|
|
||||||
visualizer_sync_interval, 30, [](unsigned v) {
|
|
||||||
lowerBoundCheck(v, 10u);
|
|
||||||
return boost::posix_time::seconds(v);
|
|
||||||
}));
|
|
||||||
p.add("visualizer_type", assign_default(
|
|
||||||
visualizer_type, VisualizerType::Wave
|
|
||||||
));
|
|
||||||
p.add("visualizer_look", assign_default<std::string>(
|
|
||||||
visualizer_chars, "●▮", [](std::string s) {
|
|
||||||
auto result = ToWString(std::move(s));
|
auto result = ToWString(std::move(s));
|
||||||
typedef std::wstring::size_type size_type;
|
boundsCheck<std::wstring::size_type>(result.size(), 2, 2);
|
||||||
boundsCheck(result.size(), size_type(2), size_type(2));
|
|
||||||
return result;
|
return result;
|
||||||
}));
|
});
|
||||||
p.add("visualizer_color", option_parser::worker([this](std::string v) {
|
p.add("visualizer_color", &visualizer_colors,
|
||||||
boost::sregex_token_iterator color(v.begin(), v.end(), boost::regex("\\w+")), end;
|
"blue, cyan, green, yellow, magenta, red", list_of<NC::Color>);
|
||||||
for (; color != end; ++color)
|
p.add("system_encoding", &system_encoding, "", [](std::string encoding) {
|
||||||
{
|
#ifdef HAVE_LANGINFO_H
|
||||||
try {
|
|
||||||
visualizer_colors.push_back(boost::lexical_cast<NC::Color>(*color));
|
|
||||||
} catch (boost::bad_lexical_cast &) {
|
|
||||||
throw std::runtime_error("invalid color: " + *color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (visualizer_colors.empty())
|
|
||||||
throw std::runtime_error("empty list");
|
|
||||||
}, [this] {
|
|
||||||
visualizer_colors = { NC::Color::Blue, NC::Color::Cyan, NC::Color::Green, NC::Color::Yellow, NC::Color::Magenta, NC::Color::Red };
|
|
||||||
}));
|
|
||||||
p.add("system_encoding", assign_default<std::string>(
|
|
||||||
system_encoding, "", [](std::string enc) {
|
|
||||||
# ifdef HAVE_LANGINFO_H
|
|
||||||
// try to autodetect system encoding
|
// try to autodetect system encoding
|
||||||
if (enc.empty())
|
if (encoding.empty())
|
||||||
{
|
{
|
||||||
enc = nl_langinfo(CODESET);
|
encoding = nl_langinfo(CODESET);
|
||||||
if (enc == "UTF-8") // mpd uses utf-8 by default so no need to convert
|
if (encoding == "UTF-8") // mpd uses utf-8 by default so no need to convert
|
||||||
enc.clear();
|
encoding.clear();
|
||||||
}
|
}
|
||||||
# endif // HAVE_LANGINFO_H
|
#endif // HAVE_LANGINFO_H
|
||||||
return enc;
|
return encoding;
|
||||||
}));
|
});
|
||||||
p.add("playlist_disable_highlight_delay", assign_default<unsigned>(
|
p.add("playlist_disable_highlight_delay", &playlist_disable_highlight_delay,
|
||||||
playlist_disable_highlight_delay, 5, [](unsigned v) {
|
"5", [](std::string v) {
|
||||||
return boost::posix_time::seconds(v);
|
return boost::posix_time::seconds(verbose_lexical_cast<unsigned>(v));
|
||||||
}));
|
});
|
||||||
p.add("message_delay_time", assign_default(
|
p.add("message_delay_time", &message_delay_time, "5");
|
||||||
message_delay_time, 5
|
p.add("song_list_format", &song_list_format,
|
||||||
));
|
"{%a - }{%t}|{$8%f$9}$R{$3(%l)$9}", [](std::string v) {
|
||||||
p.add("song_list_format", assign_default<std::string>(
|
return Format::parse(v);
|
||||||
song_list_format, "{%a - }{%t}|{$8%f$9}$R{$3(%l)$9}", [](std::string v) {
|
});
|
||||||
return Format::parse(v);
|
p.add("song_status_format", &song_status_format,
|
||||||
}));
|
"{{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}", [this](std::string v) {
|
||||||
p.add("song_status_format", assign_default<std::string>(
|
auto flags = Format::Flags::All ^ Format::Flags::OutputSwitch;
|
||||||
song_status_format, "{{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}", [this](std::string v) {
|
// precompute wide format for status display
|
||||||
const unsigned flags = Format::Flags::All ^ Format::Flags::OutputSwitch;
|
song_status_wformat = Format::parse(ToWString(v), flags);
|
||||||
// precompute wide format for status display
|
return Format::parse(v, flags);
|
||||||
song_status_wformat = Format::parse(ToWString(v), flags);
|
});
|
||||||
return Format::parse(v, flags);
|
p.add("song_library_format", &song_library_format,
|
||||||
}));
|
"{%n - }{%t}|{%f}", [](std::string v) {
|
||||||
p.add("song_library_format", assign_default<std::string>(
|
return Format::parse(v);
|
||||||
song_library_format, "{%n - }{%t}|{%f}", [](std::string v) {
|
});
|
||||||
return Format::parse(v);
|
p.add("alternative_header_first_line_format", &new_header_first_line,
|
||||||
}));
|
"$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b", [](std::string v) {
|
||||||
p.add("alternative_header_first_line_format", assign_default<std::string>(
|
return Format::parse(ToWString(std::move(v)),
|
||||||
new_header_first_line, "$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b", [](std::string v) {
|
Format::Flags::All ^ Format::Flags::OutputSwitch);
|
||||||
return Format::parse(ToWString(std::move(v)),
|
});
|
||||||
Format::Flags::All ^ Format::Flags::OutputSwitch
|
p.add("alternative_header_second_line_format", &new_header_second_line,
|
||||||
);
|
"{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}", [](std::string v) {
|
||||||
}));
|
return Format::parse(ToWString(std::move(v)),
|
||||||
p.add("alternative_header_second_line_format", assign_default<std::string>(
|
Format::Flags::All ^ Format::Flags::OutputSwitch);
|
||||||
new_header_second_line, "{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}", [](std::string v) {
|
});
|
||||||
return Format::parse(ToWString(std::move(v)),
|
p.add("now_playing_prefix", &now_playing_prefix,
|
||||||
Format::Flags::All ^ Format::Flags::OutputSwitch
|
"$b", [this](std::string v) {
|
||||||
);
|
NC::Buffer result = buffer(v);
|
||||||
}));
|
now_playing_prefix_length = wideLength(ToWString(result.str()));
|
||||||
p.add("now_playing_prefix", buffer(
|
return result;
|
||||||
now_playing_prefix, NC::Buffer::init(NC::Format::Bold), [this](NC::Buffer buf) {
|
});
|
||||||
now_playing_prefix_length = wideLength(ToWString(buf.str()));
|
p.add("now_playing_suffix", &now_playing_suffix,
|
||||||
return buf;
|
"$/b", [this](std::string v) {
|
||||||
}));
|
NC::Buffer result = buffer(v);
|
||||||
p.add("now_playing_suffix", buffer(
|
now_playing_suffix_length = wideLength(ToWString(result.str()));
|
||||||
now_playing_suffix, NC::Buffer::init(NC::Format::NoBold), [this](NC::Buffer buf) {
|
return result;
|
||||||
now_playing_suffix_length = wideLength(ToWString(buf.str()));
|
});
|
||||||
return buf;
|
p.add("browser_playlist_prefix", &browser_playlist_prefix, "$2playlist$9 ", buffer);
|
||||||
}));
|
p.add("selected_item_prefix", &selected_item_prefix,
|
||||||
p.add("browser_playlist_prefix", buffer(
|
"$6", [this](std::string v) {
|
||||||
browser_playlist_prefix, NC::Buffer::init(NC::Color::Red, "playlist", NC::Color::End, ' '), id_()
|
NC::Buffer result = buffer(v);
|
||||||
));
|
selected_item_prefix_length = wideLength(ToWString(result.str()));
|
||||||
p.add("selected_item_prefix", buffer(
|
return result;
|
||||||
selected_item_prefix, NC::Buffer::init(NC::Color::Magenta), [this](NC::Buffer buf) {
|
});
|
||||||
selected_item_prefix_length = wideLength(ToWString(buf.str()));
|
p.add("selected_item_suffix", &selected_item_suffix,
|
||||||
return buf;
|
"$9", [this](std::string v) {
|
||||||
}));
|
NC::Buffer result = buffer(v);
|
||||||
p.add("selected_item_suffix", buffer(
|
selected_item_suffix_length = wideLength(ToWString(result.str()));
|
||||||
selected_item_suffix, NC::Buffer::init(NC::Color::End), [this](NC::Buffer buf) {
|
return result;
|
||||||
selected_item_suffix_length = wideLength(ToWString(buf.str()));
|
});
|
||||||
return buf;
|
p.add("modified_item_prefix", &modified_item_prefix, "$3>$9 ", buffer);
|
||||||
}));
|
p.add("song_window_title_format", &song_window_title_format,
|
||||||
p.add("modified_item_prefix", buffer(
|
"{%a - }{%t}|{%f}", [](std::string v) {
|
||||||
modified_item_prefix, NC::Buffer::init(NC::Color::Green, "> ", NC::Color::End), id_()
|
return Format::parse(v, Format::Flags::Tag);
|
||||||
));
|
});
|
||||||
p.add("browser_sort_mode", assign_default(
|
p.add("browser_sort_mode", &browser_sort_mode, "name");
|
||||||
browser_sort_mode, SortMode::Name
|
p.add("browser_sort_format", &browser_sort_format,
|
||||||
));
|
"{%a - }{%t}|{%f} {(%l)}", [](std::string v) {
|
||||||
p.add("browser_sort_format", assign_default<std::string>(
|
return Format::parse(v, Format::Flags::Tag);
|
||||||
browser_sort_format, "{%a - }{%t}|{%f} {(%l)}", [](std::string v) {
|
});
|
||||||
return Format::parse(v, Format::Flags::Tag);
|
p.add("song_columns_list_format", &song_columns_mode_format,
|
||||||
}));
|
"(20)[]{a} (6f)[green]{NE} (50)[white]{t|f:Title} (20)[cyan]{b} (7f)[magenta]{l}",
|
||||||
p.add("song_window_title_format", assign_default<std::string>(
|
[this](std::string v) {
|
||||||
song_window_title_format, "{%a - }{%t}|{%f}", [](std::string v) {
|
columns = generate_columns(v);
|
||||||
return Format::parse(v, Format::Flags::Tag);
|
return columns_to_format(columns);
|
||||||
}));
|
});
|
||||||
p.add("song_columns_list_format", assign_default<std::string>(
|
p.add("execute_on_song_change", &execute_on_song_change, "", adjust_path);
|
||||||
columns_format, "(20)[]{a} (6f)[green]{NE} (50)[white]{t|f:Title} (20)[cyan]{b} (7f)[magenta]{l}",
|
p.add("execute_on_player_state_change", &execute_on_player_state_change,
|
||||||
[this](std::string v) {
|
"", adjust_path);
|
||||||
columns = generate_columns(v);
|
p.add("playlist_show_mpd_host", &playlist_show_mpd_host, "no", yes_no);
|
||||||
song_columns_mode_format = columns_to_format(columns);
|
p.add("playlist_show_remaining_time", &playlist_show_remaining_time, "no", yes_no);
|
||||||
return v;
|
p.add("playlist_shorten_total_times", &playlist_shorten_total_times, "no", yes_no);
|
||||||
}));
|
p.add("playlist_separate_albums", &playlist_separate_albums, "no", yes_no);
|
||||||
p.add("execute_on_song_change", assign_default<std::string>(
|
p.add("playlist_display_mode", &playlist_display_mode, "columns");
|
||||||
execute_on_song_change, "", adjust_path
|
p.add("browser_display_mode", &browser_display_mode, "classic");
|
||||||
));
|
p.add("search_engine_display_mode", &search_engine_display_mode, "classic");
|
||||||
p.add("execute_on_player_state_change", assign_default<std::string>(
|
p.add("playlist_editor_display_mode", &playlist_editor_display_mode, "classic");
|
||||||
execute_on_player_state_change, "", adjust_path
|
p.add("discard_colors_if_item_is_selected", &discard_colors_if_item_is_selected,
|
||||||
));
|
"yes", yes_no);
|
||||||
p.add("playlist_show_mpd_host", yes_no(
|
p.add("show_duplicate_tags", &MPD::Song::ShowDuplicateTags, "yes", yes_no);
|
||||||
playlist_show_mpd_host, false
|
p.add("incremental_seeking", &incremental_seeking, "yes", yes_no);
|
||||||
));
|
p.add("seek_time", &seek_time, "1");
|
||||||
p.add("playlist_show_remaining_time", yes_no(
|
p.add("volume_change_step", &volume_change_step, "2");
|
||||||
playlist_show_remaining_time, false
|
p.add("autocenter_mode", &autocenter_mode, "no", yes_no);
|
||||||
));
|
p.add("centered_cursor", ¢ered_cursor, "no", yes_no);
|
||||||
p.add("playlist_shorten_total_times", yes_no(
|
p.add("progressbar_look", &progressbar, "=>", [](std::string v) {
|
||||||
playlist_shorten_total_times, false
|
auto result = ToWString(std::move(v));
|
||||||
));
|
boundsCheck<std::wstring::size_type>(result.size(), 2, 3);
|
||||||
p.add("playlist_separate_albums", yes_no(
|
// If two characters were specified, fill \0 as the third one.
|
||||||
playlist_separate_albums, false
|
|
||||||
));
|
|
||||||
p.add("playlist_display_mode", assign_default(
|
|
||||||
playlist_display_mode, DisplayMode::Columns
|
|
||||||
));
|
|
||||||
p.add("browser_display_mode", assign_default(
|
|
||||||
browser_display_mode, DisplayMode::Classic
|
|
||||||
));
|
|
||||||
p.add("search_engine_display_mode", assign_default(
|
|
||||||
search_engine_display_mode, DisplayMode::Classic
|
|
||||||
));
|
|
||||||
p.add("playlist_editor_display_mode", assign_default(
|
|
||||||
playlist_editor_display_mode, DisplayMode::Classic
|
|
||||||
));
|
|
||||||
p.add("discard_colors_if_item_is_selected", yes_no(
|
|
||||||
discard_colors_if_item_is_selected, true
|
|
||||||
));
|
|
||||||
p.add("show_duplicate_tags", yes_no(
|
|
||||||
MPD::Song::ShowDuplicateTags, true
|
|
||||||
));
|
|
||||||
p.add("incremental_seeking", yes_no(
|
|
||||||
incremental_seeking, true
|
|
||||||
));
|
|
||||||
p.add("seek_time", assign_default(
|
|
||||||
seek_time, 1
|
|
||||||
));
|
|
||||||
p.add("volume_change_step", assign_default(
|
|
||||||
volume_change_step, 2
|
|
||||||
));
|
|
||||||
p.add("autocenter_mode", yes_no(
|
|
||||||
autocenter_mode, false
|
|
||||||
));
|
|
||||||
p.add("centered_cursor", yes_no(
|
|
||||||
centered_cursor, false
|
|
||||||
));
|
|
||||||
p.add("progressbar_look", assign_default<std::string>(
|
|
||||||
progressbar, "=>", [](std::string s) {
|
|
||||||
auto result = ToWString(std::move(s));
|
|
||||||
typedef std::wstring::size_type size_type;
|
|
||||||
boundsCheck(result.size(), size_type(2), size_type(3));
|
|
||||||
// if two characters were specified, add third one (null)
|
|
||||||
result.resize(3);
|
result.resize(3);
|
||||||
return result;
|
return result;
|
||||||
}));
|
});
|
||||||
p.add("progressbar_boldness", yes_no(
|
p.add("progressbar_boldness", &progressbar_boldness, "yes", yes_no);
|
||||||
progressbar_boldness, true
|
p.add("default_place_to_search_in", &search_in_db, "database", [](std::string v) {
|
||||||
));
|
if (v == "database")
|
||||||
p.add("default_place_to_search_in", option_parser::worker([this](std::string v) {
|
return true;
|
||||||
if (v == "database")
|
else if (v == "playlist")
|
||||||
search_in_db = true;
|
return false;
|
||||||
else if (v == "playlist")
|
else
|
||||||
search_in_db = true;
|
invalid_value(v);
|
||||||
else
|
});
|
||||||
throw std::runtime_error("invalid argument: " + v);
|
p.add("user_interface", &design, "classic");
|
||||||
}, defaults_to(search_in_db, true)
|
p.add("data_fetching_delay", &data_fetching_delay, "yes", yes_no);
|
||||||
));
|
p.add("media_library_primary_tag", &media_lib_primary_tag, "artist", [](std::string v) {
|
||||||
p.add("user_interface", assign_default(
|
if (v == "artist")
|
||||||
design, Design::Classic
|
return MPD_TAG_ARTIST;
|
||||||
));
|
else if (v == "album_artist")
|
||||||
p.add("data_fetching_delay", yes_no(
|
return MPD_TAG_ALBUM_ARTIST;
|
||||||
data_fetching_delay, true
|
else if (v == "date")
|
||||||
));
|
return MPD_TAG_DATE;
|
||||||
p.add("media_library_primary_tag", option_parser::worker([this](std::string v) {
|
else if (v == "genre")
|
||||||
if (v == "artist")
|
return MPD_TAG_GENRE;
|
||||||
media_lib_primary_tag = MPD_TAG_ARTIST;
|
else if (v == "composer")
|
||||||
else if (v == "album_artist")
|
return MPD_TAG_COMPOSER;
|
||||||
media_lib_primary_tag = MPD_TAG_ALBUM_ARTIST;
|
else if (v == "performer")
|
||||||
else if (v == "date")
|
return MPD_TAG_PERFORMER;
|
||||||
media_lib_primary_tag = MPD_TAG_DATE;
|
else
|
||||||
else if (v == "genre")
|
invalid_value(v);
|
||||||
media_lib_primary_tag = MPD_TAG_GENRE;
|
});
|
||||||
else if (v == "composer")
|
p.add("default_find_mode", &wrapped_search, "wrapped", [](std::string v) {
|
||||||
media_lib_primary_tag = MPD_TAG_COMPOSER;
|
if (v == "wrapped")
|
||||||
else if (v == "performer")
|
return true;
|
||||||
media_lib_primary_tag = MPD_TAG_PERFORMER;
|
else if (v == "normal")
|
||||||
else
|
return false;
|
||||||
throw std::runtime_error("invalid argument: " + v);
|
else
|
||||||
}, defaults_to(media_lib_primary_tag, MPD_TAG_ARTIST)
|
invalid_value(v);
|
||||||
));
|
});
|
||||||
p.add("default_find_mode", option_parser::worker([this](std::string v) {
|
p.add("default_tag_editor_pattern", &pattern, "%n - %t");
|
||||||
if (v == "wrapped")
|
p.add("header_visibility", &header_visibility, "yes", yes_no);
|
||||||
wrapped_search = true;
|
p.add("statusbar_visibility", &statusbar_visibility, "yes", yes_no);
|
||||||
else if (v == "normal")
|
p.add("titles_visibility", &titles_visibility, "yes", yes_no);
|
||||||
wrapped_search = false;
|
p.add("header_text_scrolling", &header_text_scrolling, "yes", yes_no);
|
||||||
else
|
p.add("cyclic_scrolling", &use_cyclic_scrolling, "no", yes_no);
|
||||||
throw std::runtime_error("invalid argument: " + v);
|
p.add("lines_scrolled", &lines_scrolled, "2");
|
||||||
}, defaults_to(wrapped_search, true)
|
p.add("lyrics_fetchers", &lyrics_fetchers,
|
||||||
));
|
"lyricwiki, azlyrics, genius, sing365, lyricsmania, metrolyrics, justsomelyrics, tekstowo, internet",
|
||||||
p.add("default_tag_editor_pattern", assign_default(
|
list_of<LyricsFetcher_>);
|
||||||
pattern, "%n - %t"
|
p.add("follow_now_playing_lyrics", &now_playing_lyrics, "no", yes_no);
|
||||||
));
|
p.add("fetch_lyrics_for_current_song_in_background", &fetch_lyrics_in_background,
|
||||||
p.add("header_visibility", yes_no(
|
"no", yes_no);
|
||||||
header_visibility, true
|
p.add("store_lyrics_in_song_dir", &store_lyrics_in_song_dir, "no", yes_no);
|
||||||
));
|
p.add("generate_win32_compatible_filenames", &generate_win32_compatible_filenames,
|
||||||
p.add("statusbar_visibility", yes_no(
|
"yes", yes_no);
|
||||||
statusbar_visibility, true
|
p.add("allow_for_physical_item_deletion", &allow_for_physical_item_deletion,
|
||||||
));
|
"no", yes_no);
|
||||||
p.add("titles_visibility", yes_no(
|
p.add("lastfm_preferred_language", &lastfm_preferred_language, "en");
|
||||||
titles_visibility, true
|
p.add("space_add_mode", &space_add_mode, "add_remove");
|
||||||
));
|
p.add("show_hidden_files_in_local_browser", &local_browser_show_hidden_files,
|
||||||
p.add("header_text_scrolling", yes_no(
|
"no", yes_no);
|
||||||
header_text_scrolling, true
|
p.add<void>(
|
||||||
));
|
"screen_switcher_mode", nullptr, "playlist, browser",
|
||||||
p.add("cyclic_scrolling", yes_no(
|
[this](std::string v) {
|
||||||
use_cyclic_scrolling, false
|
if (v == "previous")
|
||||||
));
|
screen_switcher_previous = true;
|
||||||
p.add("lines_scrolled", assign_default(
|
else
|
||||||
lines_scrolled, 2
|
|
||||||
));
|
|
||||||
p.add("lyrics_fetchers", option_parser::worker([this](std::string v) {
|
|
||||||
boost::sregex_token_iterator fetcher(v.begin(), v.end(), boost::regex("\\w+")), end;
|
|
||||||
for (; fetcher != end; ++fetcher)
|
|
||||||
lyrics_fetchers.push_back(toLyricsFetcher(*fetcher));
|
|
||||||
if (lyrics_fetchers.empty())
|
|
||||||
throw std::runtime_error("empty list");
|
|
||||||
}, [this] {
|
|
||||||
lyrics_fetchers.push_back(std::make_unique<LyricwikiFetcher>());
|
|
||||||
lyrics_fetchers.push_back(std::make_unique<AzLyricsFetcher>());
|
|
||||||
lyrics_fetchers.push_back(std::make_unique<GeniusFetcher>());
|
|
||||||
lyrics_fetchers.push_back(std::make_unique<Sing365Fetcher>());
|
|
||||||
lyrics_fetchers.push_back(std::make_unique<LyricsmaniaFetcher>());
|
|
||||||
lyrics_fetchers.push_back(std::make_unique<MetrolyricsFetcher>());
|
|
||||||
lyrics_fetchers.push_back(std::make_unique<JustSomeLyricsFetcher>());
|
|
||||||
lyrics_fetchers.push_back(std::make_unique<TekstowoFetcher>());
|
|
||||||
lyrics_fetchers.push_back(std::make_unique<InternetLyricsFetcher>());
|
|
||||||
}));
|
|
||||||
p.add("follow_now_playing_lyrics", yes_no(
|
|
||||||
now_playing_lyrics, false
|
|
||||||
));
|
|
||||||
p.add("fetch_lyrics_for_current_song_in_background", yes_no(
|
|
||||||
fetch_lyrics_in_background, false
|
|
||||||
));
|
|
||||||
p.add("store_lyrics_in_song_dir", yes_no(
|
|
||||||
store_lyrics_in_song_dir, false
|
|
||||||
));
|
|
||||||
p.add("generate_win32_compatible_filenames", yes_no(
|
|
||||||
generate_win32_compatible_filenames, true
|
|
||||||
));
|
|
||||||
p.add("allow_for_physical_item_deletion", yes_no(
|
|
||||||
allow_for_physical_item_deletion, false
|
|
||||||
));
|
|
||||||
p.add("lastfm_preferred_language", assign_default(
|
|
||||||
lastfm_preferred_language, "en"
|
|
||||||
));
|
|
||||||
p.add("space_add_mode", assign_default(
|
|
||||||
space_add_mode, SpaceAddMode::AlwaysAdd
|
|
||||||
));
|
|
||||||
p.add("show_hidden_files_in_local_browser", yes_no(
|
|
||||||
local_browser_show_hidden_files, false
|
|
||||||
));
|
|
||||||
p.add("screen_switcher_mode", option_parser::worker([this](std::string v) {
|
|
||||||
if (v == "previous")
|
|
||||||
screen_switcher_previous = true;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
screen_switcher_previous = false;
|
|
||||||
boost::sregex_token_iterator i(v.begin(), v.end(), boost::regex("\\w+")), j;
|
|
||||||
for (; i != j; ++i)
|
|
||||||
{
|
{
|
||||||
auto screen = stringtoStartupScreenType(*i);
|
screen_switcher_previous = false;
|
||||||
if (screen != ScreenType::Unknown)
|
screen_sequence = list_of<ScreenType>(v, [](std::string s) {
|
||||||
screen_sequence.push_back(screen);
|
auto screen = stringtoStartupScreenType(s);
|
||||||
else
|
if (screen == ScreenType::Unknown)
|
||||||
throw std::runtime_error("unknown screen: " + *i);
|
invalid_value(s);
|
||||||
|
return screen;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}, [this] {
|
p.add("startup_screen", &startup_screen_type, "playlist", [](std::string v) {
|
||||||
screen_switcher_previous = false;
|
auto screen = stringtoStartupScreenType(v);
|
||||||
screen_sequence = { ScreenType::Playlist, ScreenType::Browser };
|
if (screen == ScreenType::Unknown)
|
||||||
}));
|
invalid_value(v);
|
||||||
p.add("startup_screen", option_parser::worker([this](std::string v) {
|
return screen;
|
||||||
startup_screen_type = stringtoStartupScreenType(v);
|
});
|
||||||
if (startup_screen_type == ScreenType::Unknown)
|
p.add("startup_slave_screen", &startup_slave_screen_type, "", [](std::string v) {
|
||||||
throw std::runtime_error("unknown screen: " + v);
|
boost::optional<ScreenType> screen;
|
||||||
}, defaults_to(startup_screen_type, ScreenType::Playlist)
|
if (!v.empty())
|
||||||
));
|
{
|
||||||
p.add("startup_slave_screen", option_parser::worker([this](std::string v) {
|
screen = stringtoStartupScreenType(v);
|
||||||
if (!v.empty())
|
if (screen == ScreenType::Unknown)
|
||||||
{
|
invalid_value(v);
|
||||||
startup_slave_screen_type = stringtoStartupScreenType(v);
|
}
|
||||||
if (startup_slave_screen_type == ScreenType::Unknown)
|
return screen;
|
||||||
throw std::runtime_error("unknown slave screen: " + v);
|
});
|
||||||
}
|
p.add("startup_slave_screen_focus", &startup_slave_screen_focus, "no", yes_no);
|
||||||
}, defaults_to(startup_slave_screen_type, boost::none)
|
p.add("locked_screen_width_part", &locked_screen_width_part,
|
||||||
));
|
"50", [](std::string v) {
|
||||||
p.add("startup_slave_screen_focus", yes_no(
|
return verbose_lexical_cast<double>(v) / 100;
|
||||||
startup_slave_screen_focus, false
|
});
|
||||||
));
|
p.add("ask_for_locked_screen_width_part", &ask_for_locked_screen_width_part,
|
||||||
p.add("locked_screen_width_part", assign_default<double>(
|
"yes", yes_no);
|
||||||
locked_screen_width_part, 50.0, [](double v) {
|
p.add("jump_to_now_playing_song_at_start", &jump_to_now_playing_song_at_start,
|
||||||
return v / 100;
|
"yes", yes_no);
|
||||||
}));
|
p.add("ask_before_clearing_playlists", &ask_before_clearing_playlists,
|
||||||
p.add("ask_for_locked_screen_width_part", yes_no(
|
"yes", yes_no);
|
||||||
ask_for_locked_screen_width_part, true
|
p.add("ask_before_shuffling_playlists", &ask_before_clearing_playlists,
|
||||||
));
|
"yes", yes_no);
|
||||||
p.add("jump_to_now_playing_song_at_start", yes_no(
|
p.add("clock_display_seconds", &clock_display_seconds, "no", yes_no);
|
||||||
jump_to_now_playing_song_at_start, true
|
p.add("display_volume_level", &display_volume_level, "yes", yes_no);
|
||||||
));
|
p.add("display_bitrate", &display_bitrate, "no", yes_no);
|
||||||
p.add("ask_before_clearing_playlists", yes_no(
|
p.add("display_remaining_time", &display_remaining_time, "no", yes_no);
|
||||||
ask_before_clearing_playlists, true
|
p.add("regular_expressions", ®ex_type, "perl", [](std::string v) {
|
||||||
));
|
if (v == "none")
|
||||||
p.add("ask_before_shuffling_playlists", yes_no(
|
return boost::regex::icase | boost::regex::literal;
|
||||||
ask_before_shuffling_playlists, true
|
else if (v == "basic")
|
||||||
));
|
return boost::regex::icase | boost::regex::basic;
|
||||||
p.add("clock_display_seconds", yes_no(
|
else if (v == "extended")
|
||||||
clock_display_seconds, false
|
return boost::regex::icase | boost::regex::extended;
|
||||||
));
|
else if (v == "perl")
|
||||||
p.add("display_volume_level", yes_no(
|
return boost::regex::icase | boost::regex::perl;
|
||||||
display_volume_level, true
|
else
|
||||||
));
|
invalid_value(v);
|
||||||
p.add("display_bitrate", yes_no(
|
});
|
||||||
display_bitrate, false
|
p.add("ignore_leading_the", &ignore_leading_the, "no", yes_no);
|
||||||
));
|
p.add("block_search_constraints_change_if_items_found",
|
||||||
p.add("display_remaining_time", yes_no(
|
&block_search_constraints_change, "yes", yes_no);
|
||||||
display_remaining_time, false
|
p.add("mouse_support", &mouse_support, "yes", yes_no);
|
||||||
));
|
p.add("mouse_list_scroll_whole_page", &mouse_list_scroll_whole_page, "yes", yes_no);
|
||||||
p.add("regular_expressions", option_parser::worker([this](std::string v) {
|
p.add("empty_tag_marker", &empty_tag, "<empty>");
|
||||||
if (v == "none")
|
p.add("tags_separator", &MPD::Song::TagsSeparator, " | ");
|
||||||
regex_type = boost::regex::literal;
|
p.add("tag_editor_extended_numeration", &tag_editor_extended_numeration, "no", yes_no);
|
||||||
else if (v == "basic")
|
p.add("media_library_sort_by_mtime", &media_library_sort_by_mtime, "no", yes_no);
|
||||||
regex_type = boost::regex::basic;
|
p.add("enable_window_title", &set_window_title, "yes", [](std::string v) {
|
||||||
else if (v == "extended")
|
// Consider this variable only if TERM variable is available and we're not
|
||||||
regex_type = boost::regex::extended;
|
// in emacs terminal nor tty (through any wrapper like screen).
|
||||||
else if (v == "perl")
|
auto term = getenv("TERM");
|
||||||
regex_type = boost::regex::perl;
|
if (term != nullptr
|
||||||
else
|
&& strstr(term, "linux") == nullptr
|
||||||
throw std::runtime_error("invalid argument: " + v);
|
&& strncmp(term, "eterm", const_strlen("eterm")))
|
||||||
regex_type |= boost::regex::icase;
|
return yes_no(v);
|
||||||
}, defaults_to(regex_type, boost::regex::perl | boost::regex::icase)
|
else
|
||||||
));
|
{
|
||||||
p.add("ignore_leading_the", yes_no(
|
|
||||||
ignore_leading_the, false
|
|
||||||
));
|
|
||||||
p.add("block_search_constraints_change_if_items_found", yes_no(
|
|
||||||
block_search_constraints_change, true
|
|
||||||
));
|
|
||||||
p.add("mouse_support", yes_no(
|
|
||||||
mouse_support, true
|
|
||||||
));
|
|
||||||
p.add("mouse_list_scroll_whole_page", yes_no(
|
|
||||||
mouse_list_scroll_whole_page, true
|
|
||||||
));
|
|
||||||
p.add("empty_tag_marker", assign_default(
|
|
||||||
empty_tag, "<empty>"
|
|
||||||
));
|
|
||||||
p.add("tags_separator", assign_default(
|
|
||||||
MPD::Song::TagsSeparator, " | "
|
|
||||||
));
|
|
||||||
p.add("tag_editor_extended_numeration", yes_no(
|
|
||||||
tag_editor_extended_numeration, false
|
|
||||||
));
|
|
||||||
p.add("media_library_sort_by_mtime", yes_no(
|
|
||||||
media_library_sort_by_mtime, false
|
|
||||||
));
|
|
||||||
p.add("enable_window_title", [this]() {
|
|
||||||
// Consider this variable only if TERM variable is available
|
|
||||||
// and we're not in emacs terminal nor tty (through any wrapper
|
|
||||||
// like screen).
|
|
||||||
auto term = getenv("TERM");
|
|
||||||
if (term != nullptr
|
|
||||||
&& strstr(term, "linux") == nullptr
|
|
||||||
&& strncmp(term, "eterm", const_strlen("eterm")))
|
|
||||||
return yes_no(set_window_title, true);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
set_window_title = false;
|
|
||||||
return option_parser::worker([](std::string) {}, [] {
|
|
||||||
std::clog << "Terminal doesn't support window title, skipping 'enable_window_title'.\n";
|
std::clog << "Terminal doesn't support window title, skipping 'enable_window_title'.\n";
|
||||||
});
|
return false;
|
||||||
}
|
}
|
||||||
}());
|
});
|
||||||
p.add("search_engine_default_search_mode", assign_default<unsigned>(
|
p.add("search_engine_default_search_mode", &search_engine_default_search_mode,
|
||||||
search_engine_default_search_mode, 1, [](unsigned v) {
|
"1", [](std::string v) {
|
||||||
boundsCheck(v, 1u, 3u);
|
auto mode = verbose_lexical_cast<unsigned>(v);
|
||||||
return --v;
|
boundsCheck<unsigned>(mode, 1, 3);
|
||||||
}));
|
return --mode;
|
||||||
p.add("external_editor", assign_default<std::string>(
|
});
|
||||||
external_editor, "nano", adjust_path
|
p.add("external_editor", &external_editor, "nano", adjust_path);
|
||||||
));
|
p.add("use_console_editor", &use_console_editor, "yes", yes_no);
|
||||||
p.add("use_console_editor", yes_no(
|
p.add("colors_enabled", &colors_enabled, "yes", yes_no);
|
||||||
use_console_editor, true
|
p.add("empty_tag_color", &empty_tags_color, "cyan");
|
||||||
));
|
p.add("header_window_color", &header_color, "default");
|
||||||
p.add("colors_enabled", yes_no(
|
p.add("volume_color", &volume_color, "default");
|
||||||
colors_enabled, true
|
p.add("state_line_color", &state_line_color, "default");
|
||||||
));
|
p.add("state_flags_color", &state_flags_color, "default");
|
||||||
p.add("empty_tag_color", assign_default(
|
p.add("main_window_color", &main_color, "yellow");
|
||||||
empty_tags_color, NC::Color::Cyan
|
p.add("color1", &color1, "white");
|
||||||
));
|
p.add("color2", &color2, "green");
|
||||||
p.add("header_window_color", assign_default(
|
p.add("main_window_highlight_color", &main_highlight_color, "yellow");
|
||||||
header_color, NC::Color::Default
|
p.add("progressbar_color", &progressbar_color, "black");
|
||||||
));
|
p.add("progressbar_elapsed_color", &progressbar_elapsed_color, "green");
|
||||||
p.add("volume_color", assign_default(
|
p.add("statusbar_color", &statusbar_color, "default");
|
||||||
volume_color, NC::Color::Default
|
p.add("alternative_ui_separator_color", &alternative_ui_separator_color, "black");
|
||||||
));
|
p.add("active_column_color", &active_column_color, "red");
|
||||||
p.add("state_line_color", assign_default(
|
p.add("window_border_color", &window_border, "green", verbose_lexical_cast<NC::Color>);
|
||||||
state_line_color, NC::Color::Default
|
p.add("active_window_border", &active_window_border, "red",
|
||||||
));
|
verbose_lexical_cast<NC::Color>);
|
||||||
p.add("state_flags_color", assign_default(
|
|
||||||
state_flags_color, NC::Color::Default
|
|
||||||
));
|
|
||||||
p.add("main_window_color", assign_default(
|
|
||||||
main_color, NC::Color::Yellow
|
|
||||||
));
|
|
||||||
p.add("color1", assign_default(
|
|
||||||
color1, NC::Color::White
|
|
||||||
));
|
|
||||||
p.add("color2", assign_default(
|
|
||||||
color2, NC::Color::Green
|
|
||||||
));
|
|
||||||
p.add("main_window_highlight_color", assign_default(
|
|
||||||
main_highlight_color, NC::Color::Yellow
|
|
||||||
));
|
|
||||||
p.add("progressbar_color", assign_default(
|
|
||||||
progressbar_color, NC::Color::Black
|
|
||||||
));
|
|
||||||
p.add("progressbar_elapsed_color", assign_default(
|
|
||||||
progressbar_elapsed_color, NC::Color::Green
|
|
||||||
));
|
|
||||||
p.add("statusbar_color", assign_default(
|
|
||||||
statusbar_color, NC::Color::Default
|
|
||||||
));
|
|
||||||
p.add("alternative_ui_separator_color", assign_default(
|
|
||||||
alternative_ui_separator_color, NC::Color::Black
|
|
||||||
));
|
|
||||||
p.add("active_column_color", assign_default(
|
|
||||||
active_column_color, NC::Color::Red
|
|
||||||
));
|
|
||||||
p.add("window_border_color", border(
|
|
||||||
window_border, NC::Color::Green
|
|
||||||
));
|
|
||||||
p.add("active_window_border", border(
|
|
||||||
active_window_border, NC::Color::Red
|
|
||||||
));
|
|
||||||
|
|
||||||
return std::all_of(
|
return std::all_of(
|
||||||
config_paths.begin(),
|
config_paths.begin(),
|
||||||
@@ -739,5 +519,3 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
|
|||||||
}
|
}
|
||||||
) && p.initialize_undefined(ignore_errors);
|
) && p.initialize_undefined(ignore_errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */
|
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ struct Configuration
|
|||||||
|
|
||||||
ScreenType startup_screen_type;
|
ScreenType startup_screen_type;
|
||||||
boost::optional<ScreenType> startup_slave_screen_type;
|
boost::optional<ScreenType> startup_slave_screen_type;
|
||||||
std::list<ScreenType> screen_sequence;
|
std::vector<ScreenType> screen_sequence;
|
||||||
|
|
||||||
SortMode browser_sort_mode;
|
SortMode browser_sort_mode;
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,18 @@
|
|||||||
|
|
||||||
#include "utility/option_parser.h"
|
#include "utility/option_parser.h"
|
||||||
|
|
||||||
|
bool yes_no(std::string v)
|
||||||
|
{
|
||||||
|
if (v == "yes")
|
||||||
|
return true;
|
||||||
|
else if (v == "no")
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
invalid_value(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
bool option_parser::run(std::istream &is, bool ignore_errors)
|
bool option_parser::run(std::istream &is, bool ignore_errors)
|
||||||
{
|
{
|
||||||
// quoted value. leftmost and rightmost quotation marks are the delimiters.
|
// quoted value. leftmost and rightmost quotation marks are the delimiters.
|
||||||
@@ -52,10 +64,14 @@ bool option_parser::run(std::istream &is, bool ignore_errors)
|
|||||||
auto it = m_parsers.find(option);
|
auto it = m_parsers.find(option);
|
||||||
if (it != m_parsers.end())
|
if (it != m_parsers.end())
|
||||||
{
|
{
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
it->second.parse(match[2]);
|
it->second.parse(match[2]);
|
||||||
} catch (std::exception &e) {
|
}
|
||||||
std::cerr << "Error while processing option \"" << option << "\": " << e.what() << "\n";
|
catch (std::exception &e)
|
||||||
|
{
|
||||||
|
std::cerr << "Error while processing option \"" << option
|
||||||
|
<< "\": " << e.what() << "\n";
|
||||||
if (!ignore_errors)
|
if (!ignore_errors)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -73,14 +89,19 @@ bool option_parser::run(std::istream &is, bool ignore_errors)
|
|||||||
|
|
||||||
bool option_parser::initialize_undefined(bool ignore_errors)
|
bool option_parser::initialize_undefined(bool ignore_errors)
|
||||||
{
|
{
|
||||||
for (auto &p : m_parsers)
|
for (auto &pp : m_parsers)
|
||||||
{
|
{
|
||||||
if (!p.second.defined())
|
auto &p = pp.second;
|
||||||
|
if (!p.used())
|
||||||
{
|
{
|
||||||
try {
|
try
|
||||||
p.second.run_default();
|
{
|
||||||
} catch (std::exception &e) {
|
p.parse_default();
|
||||||
std::cerr << "Error while initializing option \"" << p.first << "\": " << e.what() << "\n";
|
}
|
||||||
|
catch (std::exception &e)
|
||||||
|
{
|
||||||
|
std::cerr << "Error while initializing option \"" << pp.first
|
||||||
|
<< "\": " << e.what() << "\n";
|
||||||
if (ignore_errors)
|
if (ignore_errors)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -88,15 +109,3 @@ bool option_parser::initialize_undefined(bool ignore_errors)
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
option_parser::worker yes_no(bool &arg, bool value)
|
|
||||||
{
|
|
||||||
return option_parser::worker([&arg](std::string &&v) {
|
|
||||||
if (v == "yes")
|
|
||||||
arg = true;
|
|
||||||
else if (v == "no")
|
|
||||||
arg = false;
|
|
||||||
else
|
|
||||||
throw std::runtime_error("invalid argument: " + v);
|
|
||||||
}, defaults_to(arg, value));
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -33,152 +33,144 @@
|
|||||||
#ifndef NCMPCPP_UTILITY_OPTION_PARSER_H
|
#ifndef NCMPCPP_UTILITY_OPTION_PARSER_H
|
||||||
#define NCMPCPP_UTILITY_OPTION_PARSER_H
|
#define NCMPCPP_UTILITY_OPTION_PARSER_H
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/trim.hpp>
|
||||||
|
#include <boost/tokenizer.hpp>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "utility/functional.h"
|
[[noreturn]] inline void invalid_value(const std::string &v)
|
||||||
|
|
||||||
struct option_parser
|
|
||||||
{
|
{
|
||||||
typedef std::function<void(std::string &&)> parser_t;
|
throw std::runtime_error("invalid value: " + v);
|
||||||
typedef std::function<void()> default_t;
|
}
|
||||||
|
|
||||||
template <typename DestT, typename SourceT>
|
template <typename DestT>
|
||||||
struct assign_value_once
|
DestT verbose_lexical_cast(std::string v)
|
||||||
{
|
{
|
||||||
typedef DestT dest_type;
|
try {
|
||||||
typedef typename std::decay<SourceT>::type source_type;
|
return boost::lexical_cast<DestT>(std::move(v));
|
||||||
|
} catch (boost::bad_lexical_cast &) {
|
||||||
|
invalid_value(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename ArgT>
|
template <typename ValueT, typename ConvertT>
|
||||||
assign_value_once(DestT &dest, ArgT &&value)
|
std::vector<ValueT> list_of(const std::string &v, ConvertT convert)
|
||||||
: m_dest(dest), m_source(std::make_shared<source_type>(std::forward<ArgT>(value))) { }
|
{
|
||||||
|
std::vector<ValueT> result;
|
||||||
|
boost::tokenizer<boost::escaped_list_separator<char>> elems(v);
|
||||||
|
for (auto &value : elems)
|
||||||
|
result.push_back(convert(boost::trim_copy(value)));
|
||||||
|
if (result.empty())
|
||||||
|
throw std::runtime_error("empty list");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void operator()()
|
template <typename ValueT>
|
||||||
{
|
std::vector<ValueT> list_of(const std::string &v)
|
||||||
assert(m_source.get() != nullptr);
|
{
|
||||||
m_dest = std::move(*m_source);
|
return list_of<ValueT>(v, verbose_lexical_cast<ValueT>);
|
||||||
m_source.reset();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
bool yes_no(std::string v);
|
||||||
DestT &m_dest;
|
|
||||||
std::shared_ptr<source_type> m_source;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename IntermediateT, typename DestT, typename TransformT>
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
struct parse_and_transform
|
|
||||||
{
|
|
||||||
template <typename ArgT>
|
|
||||||
parse_and_transform(DestT &dest, ArgT &&map)
|
|
||||||
: m_dest(dest), m_map(std::forward<ArgT>(map)) { }
|
|
||||||
|
|
||||||
void operator()(std::string &&v)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
m_dest = m_map(boost::lexical_cast<IntermediateT>(v));
|
|
||||||
} catch (boost::bad_lexical_cast &) {
|
|
||||||
throw std::runtime_error("invalid value: " + v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
DestT &m_dest;
|
|
||||||
TransformT m_map;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
class option_parser
|
||||||
|
{
|
||||||
|
template <typename DestT>
|
||||||
struct worker
|
struct worker
|
||||||
{
|
{
|
||||||
worker() { }
|
template <typename MapT>
|
||||||
|
worker(DestT *dest, MapT &&map)
|
||||||
|
: m_dest(dest), m_map(std::forward<MapT>(map)), m_dest_set(false)
|
||||||
|
{ }
|
||||||
|
|
||||||
template <typename ParserT, typename DefaultT>
|
void operator()(std::string value)
|
||||||
worker(ParserT &&p, DefaultT &&d)
|
|
||||||
: m_defined(false), m_parser(std::forward<ParserT>(p))
|
|
||||||
, m_default(std::forward<DefaultT>(d)) { }
|
|
||||||
|
|
||||||
template <typename ValueT>
|
|
||||||
void parse(ValueT &&value)
|
|
||||||
{
|
{
|
||||||
if (m_defined)
|
if (m_dest_set)
|
||||||
throw std::runtime_error("option already defined");
|
throw std::runtime_error("option already set");
|
||||||
m_parser(std::forward<ValueT>(value));
|
assign<DestT, void>::apply(m_dest, m_map, value);
|
||||||
m_defined = true;
|
m_dest_set = true;
|
||||||
}
|
|
||||||
|
|
||||||
bool defined() const
|
|
||||||
{
|
|
||||||
return m_defined;
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_default()
|
|
||||||
{
|
|
||||||
if (m_defined)
|
|
||||||
throw std::runtime_error("option already defined");
|
|
||||||
m_default();
|
|
||||||
m_defined = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_defined;
|
template <typename ValueT, typename VoidT>
|
||||||
parser_t m_parser;
|
struct assign {
|
||||||
default_t m_default;
|
static void apply(ValueT *dest,
|
||||||
|
std::function<DestT(std::string)> &map,
|
||||||
|
std::string &value) {
|
||||||
|
*dest = map(std::move(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <typename VoidT>
|
||||||
|
struct assign<void, VoidT> {
|
||||||
|
static void apply(void *,
|
||||||
|
std::function<void(std::string)> &map,
|
||||||
|
std::string &value) {
|
||||||
|
map(std::move(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DestT *m_dest;
|
||||||
|
std::function<DestT(std::string)> m_map;
|
||||||
|
bool m_dest_set;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename ParserT, typename DefaultT>
|
struct parser {
|
||||||
void add(const std::string &option, ParserT &&p, DefaultT &&d)
|
template <typename StringT, typename SetterT>
|
||||||
|
parser(StringT &&default_, SetterT &&setter_)
|
||||||
|
: m_used(false)
|
||||||
|
, m_default_value(std::forward<StringT>(default_))
|
||||||
|
, m_worker(std::forward<SetterT>(setter_))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool used() const
|
||||||
|
{
|
||||||
|
return m_used;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse(std::string v)
|
||||||
|
{
|
||||||
|
m_worker(std::move(v));
|
||||||
|
m_used = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_default() const
|
||||||
|
{
|
||||||
|
assert(!m_used);
|
||||||
|
m_worker(m_default_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_used;
|
||||||
|
std::string m_default_value;
|
||||||
|
std::function<void(std::string)> m_worker;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<std::string, parser> m_parsers;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename DestT, typename MapT>
|
||||||
|
void add(std::string option, DestT *dest, std::string default_, MapT &&map)
|
||||||
{
|
{
|
||||||
assert(m_parsers.count(option) == 0);
|
assert(m_parsers.count(option) == 0);
|
||||||
m_parsers[option] = worker(std::forward<ParserT>(p), std::forward<DefaultT>(d));
|
m_parsers.emplace(
|
||||||
|
std::move(option),
|
||||||
|
parser(
|
||||||
|
std::move(default_),
|
||||||
|
worker<DestT>(dest, std::forward<MapT>(map))));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename WorkerT>
|
template <typename DestT>
|
||||||
void add(const std::string &option, WorkerT &&w)
|
void add(std::string option, DestT *dest, std::string default_)
|
||||||
{
|
{
|
||||||
assert(m_parsers.count(option) == 0);
|
add(std::move(option), dest, std::move(default_), verbose_lexical_cast<DestT>);
|
||||||
m_parsers[option] = std::forward<WorkerT>(w);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool run(std::istream &is, bool ignore_errors);
|
bool run(std::istream &is, bool warn_on_errors);
|
||||||
bool initialize_undefined(bool ignore_errors);
|
bool initialize_undefined(bool warn_on_errors);
|
||||||
|
|
||||||
private:
|
|
||||||
std::unordered_map<std::string, worker> m_parsers;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename IntermediateT, typename ArgT, typename TransformT>
|
|
||||||
option_parser::parser_t assign(ArgT &arg, TransformT &&map = id_())
|
|
||||||
{
|
|
||||||
return option_parser::parse_and_transform<IntermediateT, ArgT, TransformT>(
|
|
||||||
arg, std::forward<TransformT>(map)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ArgT, typename ValueT>
|
|
||||||
option_parser::default_t defaults_to(ArgT &arg, ValueT &&value)
|
|
||||||
{
|
|
||||||
return option_parser::assign_value_once<ArgT, ValueT>(
|
|
||||||
arg, std::forward<ValueT>(value)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename IntermediateT, typename ArgT, typename ValueT, typename TransformT>
|
|
||||||
option_parser::worker assign_default(ArgT &arg, ValueT &&value, TransformT &&map)
|
|
||||||
{
|
|
||||||
return option_parser::worker(
|
|
||||||
assign<IntermediateT>(arg, std::forward<TransformT>(map)),
|
|
||||||
defaults_to(arg, map(std::forward<ValueT>(value)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ArgT, typename ValueT>
|
|
||||||
option_parser::worker assign_default(ArgT &arg, ValueT &&value)
|
|
||||||
{
|
|
||||||
return assign_default<ArgT>(arg, std::forward<ValueT>(value), id_());
|
|
||||||
}
|
|
||||||
|
|
||||||
// workers for specific types
|
|
||||||
|
|
||||||
option_parser::worker yes_no(bool &arg, bool value);
|
|
||||||
|
|
||||||
#endif // NCMPCPP_UTILITY_OPTION_PARSER_H
|
#endif // NCMPCPP_UTILITY_OPTION_PARSER_H
|
||||||
|
|||||||
Reference in New Issue
Block a user