settings: configuration file processing rewrite

This commit is contained in:
Andrzej Rybczak
2014-08-28 18:57:16 +02:00
parent 8a1e4a48dd
commit 4b933b29e1
35 changed files with 1881 additions and 1446 deletions

View File

@@ -2,6 +2,7 @@ bin_PROGRAMS = ncmpcpp
ncmpcpp_SOURCES = \
utility/comparators.cpp \
utility/html.cpp \
utility/option_parser.cpp \
utility/string.cpp \
utility/type_conversions.cpp \
utility/wide_string.cpp \
@@ -10,9 +11,10 @@ ncmpcpp_SOURCES = \
browser.cpp \
charset.cpp \
clock.cpp \
cmdargs.cpp \
configuration.cpp \
curl_handle.cpp \
display.cpp \
enums.cpp \
error.cpp \
global.cpp \
help.cpp \
@@ -56,7 +58,9 @@ ncmpcpp_LDFLAGS = $(all_libraries)
noinst_HEADERS = \
utility/comparators.h \
utility/conversion.h \
utility/functional.h \
utility/html.h \
utility/option_parser.h \
utility/string.h \
utility/type_conversions.h \
utility/wide_string.h \
@@ -64,9 +68,10 @@ noinst_HEADERS = \
browser.h \
charset.h \
clock.h \
cmdargs.h \
configuration.h \
curl_handle.h \
display.h \
enums.h \
error.h \
exec_item.h \
global.h \

View File

@@ -198,7 +198,7 @@ void resizeScreen(bool reload_main_window)
}
# endif
MainHeight = LINES-(Config.new_design ? 7 : 4);
MainHeight = LINES-(Config.design == Design::Alternative ? 7 : 4);
validateScreenSize();
@@ -211,7 +211,7 @@ void resizeScreen(bool reload_main_window)
applyToVisibleWindows(&BaseScreen::resize);
if (Config.header_visibility || Config.new_design)
if (Config.header_visibility || Config.design == Design::Alternative)
wHeader->resize(COLS, HeaderHeight);
FooterStartY = LINES-(Config.statusbar_visibility ? 2 : 1);
@@ -236,8 +236,8 @@ void setWindowsDimensions()
using Global::MainStartY;
using Global::MainHeight;
MainStartY = Config.new_design ? 5 : 2;
MainHeight = LINES-(Config.new_design ? 7 : 4);
MainStartY = Config.design == Design::Alternative ? 5 : 2;
MainHeight = LINES-(Config.design == Design::Alternative ? 7 : 4);
if (!Config.header_visibility)
{
@@ -247,7 +247,7 @@ void setWindowsDimensions()
if (!Config.statusbar_visibility)
++MainHeight;
HeaderHeight = Config.new_design ? (Config.header_visibility ? 5 : 3) : 1;
HeaderHeight = Config.design == Design::Alternative ? (Config.header_visibility ? 5 : 3) : 1;
FooterStartY = LINES-(Config.statusbar_visibility ? 2 : 1);
FooterHeight = Config.statusbar_visibility ? 2 : 1;
}
@@ -337,15 +337,16 @@ void MouseEvent::run()
myPlaylist->currentSongLength()*m_mouse_event.x/double(COLS));
}
else if (m_mouse_event.bstate & BUTTON1_PRESSED
&& (Config.statusbar_visibility || Config.new_design)
&& Status::State::player() != MPD::psStop
&& m_mouse_event.y == (Config.new_design ? 1 : LINES-1) && m_mouse_event.x < 9
&& (Config.statusbar_visibility || Config.design == Design::Alternative)
&& Status::State::player() != MPD::psStop
&& m_mouse_event.y == (Config.design == Design::Alternative ? 1 : LINES-1)
&& m_mouse_event.x < 9
) // playing/paused
{
Mpd.Toggle();
}
else if ((m_mouse_event.bstate & BUTTON2_PRESSED || m_mouse_event.bstate & BUTTON4_PRESSED)
&& (Config.header_visibility || Config.new_design)
&& (Config.header_visibility || Config.design == Design::Alternative)
&& m_mouse_event.y == 0 && size_t(m_mouse_event.x) > COLS-VolumeState.length()
) // volume
{
@@ -488,15 +489,24 @@ void MoveEnd::run()
void ToggleInterface::run()
{
Config.new_design = !Config.new_design;
Config.statusbar_visibility = Config.new_design ? 0 : OriginalStatusbarVisibility;
switch (Config.design)
{
case Design::Classic:
Config.design = Design::Alternative;
Config.statusbar_visibility = false;
break;
case Design::Alternative:
Config.design = Design::Classic;
Config.statusbar_visibility = OriginalStatusbarVisibility;
break;
}
setWindowsDimensions();
Progressbar::unlock();
Statusbar::unlock();
resizeScreen(false);
Status::Changes::mixer();
Status::Changes::elapsedTime(false);
Statusbar::printf("User interface: %1%", Config.new_design ? "Alternative" : "Classic");
Statusbar::printf("User interface: %1%", Config.design);
}
bool JumpToParentDirectory::canBeRun() const
@@ -606,13 +616,13 @@ void SlaveScreen::run()
void VolumeUp::run()
{
int volume = std::min(Status::State::volume()+Config.volume_change_step, 100);
int volume = std::min(Status::State::volume()+Config.volume_change_step, 100u);
Mpd.SetVolume(volume);
}
void VolumeDown::run()
{
int volume = std::max(Status::State::volume()-Config.volume_change_step, 0);
int volume = std::max(int(Status::State::volume()-Config.volume_change_step), 0);
Mpd.SetVolume(volume);
}
@@ -999,52 +1009,83 @@ void ToggleDisplayMode::run()
{
if (myScreen == myPlaylist)
{
Config.columns_in_playlist = !Config.columns_in_playlist;
Statusbar::printf("Playlist display mode: %1%",
Config.columns_in_playlist ? "Columns" : "Classic"
);
if (Config.columns_in_playlist)
switch (Config.playlist_display_mode)
{
myPlaylist->main().setItemDisplayer(boost::bind(Display::SongsInColumns, _1, myPlaylist->proxySongList()));
if (Config.titles_visibility)
myPlaylist->main().setTitle(Display::Columns(myPlaylist->main().getWidth()));
else
case DisplayMode::Classic:
Config.playlist_display_mode = DisplayMode::Columns;
myPlaylist->main().setItemDisplayer(boost::bind(
Display::SongsInColumns, _1, myPlaylist->proxySongList()
));
if (Config.titles_visibility)
myPlaylist->main().setTitle(Display::Columns(myPlaylist->main().getWidth()));
else
myPlaylist->main().setTitle("");
break;
case DisplayMode::Columns:
Config.playlist_display_mode = DisplayMode::Classic;
myPlaylist->main().setItemDisplayer(boost::bind(
Display::Songs, _1, myPlaylist->proxySongList(), Config.song_list_format
));
myPlaylist->main().setTitle("");
}
else
{
myPlaylist->main().setItemDisplayer(boost::bind(Display::Songs, _1, myPlaylist->proxySongList(), Config.song_list_format));
myPlaylist->main().setTitle("");
}
Statusbar::printf("Playlist display mode: %1%", Config.playlist_display_mode);
}
else if (myScreen == myBrowser)
{
Config.columns_in_browser = !Config.columns_in_browser;
Statusbar::printf("Browser display mode: %1%",
Config.columns_in_browser ? "Columns" : "Classic"
);
myBrowser->main().setTitle(Config.columns_in_browser && Config.titles_visibility ? Display::Columns(myBrowser->main().getWidth()) : "");
switch (Config.browser_display_mode)
{
case DisplayMode::Classic:
Config.browser_display_mode = DisplayMode::Columns;
if (Config.titles_visibility)
myBrowser->main().setTitle(Display::Columns(myBrowser->main().getWidth()));
else
myBrowser->main().setTitle("");
break;
case DisplayMode::Columns:
Config.browser_display_mode = DisplayMode::Classic;
myBrowser->main().setTitle("");
break;
}
Statusbar::printf("Browser display mode: %1%", Config.browser_display_mode);
}
else if (myScreen == mySearcher)
{
Config.columns_in_search_engine = !Config.columns_in_search_engine;
Statusbar::printf("Search engine display mode: %1%",
Config.columns_in_search_engine ? "Columns" : "Classic"
);
switch (Config.search_engine_display_mode)
{
case DisplayMode::Classic:
Config.search_engine_display_mode = DisplayMode::Columns;
break;
case DisplayMode::Columns:
Config.search_engine_display_mode = DisplayMode::Classic;
break;
}
Statusbar::printf("Search engine display mode: %1%", Config.search_engine_display_mode);
if (mySearcher->main().size() > SearchEngine::StaticOptions)
mySearcher->main().setTitle(Config.columns_in_search_engine && Config.titles_visibility ? Display::Columns(mySearcher->main().getWidth()) : "");
mySearcher->main().setTitle(
Config.search_engine_display_mode == DisplayMode::Columns
&& Config.titles_visibility
? Display::Columns(mySearcher->main().getWidth())
: ""
);
}
else if (myScreen->isActiveWindow(myPlaylistEditor->Content))
{
Config.columns_in_playlist_editor = !Config.columns_in_playlist_editor;
Statusbar::printf("Playlist editor display mode: %1%",
Config.columns_in_playlist_editor ? "Columns" : "Classic"
);
if (Config.columns_in_playlist_editor)
myPlaylistEditor->Content.setItemDisplayer(boost::bind(Display::SongsInColumns, _1, myPlaylistEditor->contentProxyList()));
else
myPlaylistEditor->Content.setItemDisplayer(boost::bind(Display::Songs, _1, myPlaylistEditor->contentProxyList(), Config.song_list_format));
switch (Config.playlist_editor_display_mode)
{
case DisplayMode::Classic:
Config.playlist_editor_display_mode = DisplayMode::Columns;
myPlaylistEditor->Content.setItemDisplayer(boost::bind(
Display::SongsInColumns, _1, myPlaylistEditor->contentProxyList()
));
break;
case DisplayMode::Columns:
Config.playlist_editor_display_mode = DisplayMode::Classic;
myPlaylistEditor->Content.setItemDisplayer(boost::bind(
Display::Songs, _1, myPlaylistEditor->contentProxyList(), Config.song_list_format
));
break;
}
Statusbar::printf("Playlist editor display mode: %1%", Config.playlist_editor_display_mode);
}
}
@@ -1688,7 +1729,7 @@ void AddSelectedItems::run()
void CropMainPlaylist::run()
{
bool yes = true;
if (Config.ask_before_clearing_main_playlist)
if (Config.ask_before_clearing_playlists)
yes = askYesNoQuestion("Do you really want to crop main playlist?", Status::trace);
if (yes)
{
@@ -1707,7 +1748,7 @@ void CropPlaylist::run()
assert(!myPlaylistEditor->Playlists.empty());
std::string playlist = myPlaylistEditor->Playlists.current().value();
bool yes = true;
if (Config.ask_before_clearing_main_playlist)
if (Config.ask_before_clearing_playlists)
yes = askYesNoQuestion(
boost::format("Do you really want to crop playlist \"%1%\"?") % playlist,
Status::trace
@@ -1724,7 +1765,7 @@ void CropPlaylist::run()
void ClearMainPlaylist::run()
{
bool yes = true;
if (Config.ask_before_clearing_main_playlist)
if (Config.ask_before_clearing_playlists)
yes = askYesNoQuestion("Do you really want to clear main playlist?", Status::trace);
if (yes)
{
@@ -1747,7 +1788,7 @@ void ClearPlaylist::run()
assert(!myPlaylistEditor->Playlists.empty());
std::string playlist = myPlaylistEditor->Playlists.current().value();
bool yes = true;
if (Config.ask_before_clearing_main_playlist)
if (Config.ask_before_clearing_playlists)
yes = askYesNoQuestion(
boost::format("Do you really want to clear playlist \"%1%\"?") % playlist,
Status::trace
@@ -1937,10 +1978,19 @@ void ToggleSpaceMode::run()
void ToggleAddMode::run()
{
Config.ncmpc_like_songs_adding = !Config.ncmpc_like_songs_adding;
Statusbar::printf("Add mode: %1%",
Config.ncmpc_like_songs_adding ? "Add item to playlist or remove if already added" : "Always add item to playlist"
);
std::string mode_desc;
switch (Config.space_add_mode)
{
case SpaceAddMode::AddRemove:
Config.space_add_mode = SpaceAddMode::AlwaysAdd;
mode_desc = "Always add an item to playlist";
break;
case SpaceAddMode::AlwaysAdd:
Config.space_add_mode = SpaceAddMode::AddRemove;
mode_desc = "Add an item to playlist or remove if already added";
break;
}
Statusbar::printf("Add mode: %1%", mode_desc);
}
void ToggleMouse::run()
@@ -2008,16 +2058,16 @@ void ToggleBrowserSortMode::run()
{
switch (Config.browser_sort_mode)
{
case smName:
Config.browser_sort_mode = smMTime;
case SortMode::Name:
Config.browser_sort_mode = SortMode::ModificationTime;
Statusbar::print("Sort songs by: Modification time");
break;
case smMTime:
Config.browser_sort_mode = smCustomFormat;
case SortMode::ModificationTime:
Config.browser_sort_mode = SortMode::CustomFormat;
Statusbar::print("Sort songs by: Custom format");
break;
case smCustomFormat:
Config.browser_sort_mode = smName;
case SortMode::CustomFormat:
Config.browser_sort_mode = SortMode::Name;
Statusbar::print("Sort songs by: Name");
break;
}
@@ -2211,13 +2261,12 @@ void NextScreen::run()
if (auto tababble = dynamic_cast<Tabbable *>(myScreen))
tababble->switchToPreviousScreen();
}
else if (!Config.screens_seq.empty())
else if (!Config.screen_sequence.empty())
{
auto screen_type = std::find(Config.screens_seq.begin(), Config.screens_seq.end(),
myScreen->type()
);
if (++screen_type == Config.screens_seq.end())
toScreen(Config.screens_seq.front())->switchTo();
const auto &seq = Config.screen_sequence;
auto screen_type = std::find(seq.begin(), seq.end(), myScreen->type());
if (++screen_type == seq.end())
toScreen(seq.front())->switchTo();
else
toScreen(*screen_type)->switchTo();
}
@@ -2230,13 +2279,12 @@ void PreviousScreen::run()
if (auto tababble = dynamic_cast<Tabbable *>(myScreen))
tababble->switchToPreviousScreen();
}
else if (!Config.screens_seq.empty())
else if (!Config.screen_sequence.empty())
{
auto screen_type = std::find(Config.screens_seq.begin(), Config.screens_seq.end(),
myScreen->type()
);
if (screen_type == Config.screens_seq.begin())
toScreen(Config.screens_seq.back())->switchTo();
const auto &seq = Config.screen_sequence;
auto screen_type = std::find(seq.begin(), seq.end(), myScreen->type());
if (screen_type == seq.begin())
toScreen(seq.back())->switchTo();
else
toScreen(*--screen_type)->switchTo();
}
@@ -2635,34 +2683,35 @@ void seek()
*wFooter << NC::Format::Bold;
std::string tracklength;
if (Config.new_design)
switch (Config.design)
{
if (Config.display_remaining_time)
{
tracklength = "-";
tracklength += MPD::Song::ShowTime(myPlaylist->currentSongLength()-songpos);
}
else
tracklength = MPD::Song::ShowTime(songpos);
tracklength += "/";
tracklength += MPD::Song::ShowTime(myPlaylist->currentSongLength());
*wHeader << NC::XY(0, 0) << tracklength << " ";
wHeader->refresh();
}
else
{
tracklength = " [";
if (Config.display_remaining_time)
{
tracklength += "-";
tracklength += MPD::Song::ShowTime(myPlaylist->currentSongLength()-songpos);
}
else
tracklength += MPD::Song::ShowTime(songpos);
tracklength += "/";
tracklength += MPD::Song::ShowTime(myPlaylist->currentSongLength());
tracklength += "]";
*wFooter << NC::XY(wFooter->getWidth()-tracklength.length(), 1) << tracklength;
case Design::Classic:
tracklength = " [";
if (Config.display_remaining_time)
{
tracklength += "-";
tracklength += MPD::Song::ShowTime(myPlaylist->currentSongLength()-songpos);
}
else
tracklength += MPD::Song::ShowTime(songpos);
tracklength += "/";
tracklength += MPD::Song::ShowTime(myPlaylist->currentSongLength());
tracklength += "]";
*wFooter << NC::XY(wFooter->getWidth()-tracklength.length(), 1) << tracklength;
break;
case Design::Alternative:
if (Config.display_remaining_time)
{
tracklength = "-";
tracklength += MPD::Song::ShowTime(myPlaylist->currentSongLength()-songpos);
}
else
tracklength = MPD::Song::ShowTime(songpos);
tracklength += "/";
tracklength += MPD::Song::ShowTime(myPlaylist->currentSongLength());
*wHeader << NC::XY(0, 0) << tracklength << " ";
wHeader->refresh();
break;
}
*wFooter << NC::Format::NoBold;
Progressbar::draw(songpos, myPlaylist->currentSongLength());

View File

@@ -99,10 +99,6 @@ struct Binding
return m_actions[0];
}
const ActionChain &actions() const {
return m_actions;
}
private:
ActionChain m_actions;
};

View File

@@ -40,6 +40,7 @@
#include "tags.h"
#include "utility/comparators.h"
#include "utility/string.h"
#include "configuration.h"
using Global::MainHeight;
using Global::MainStartY;
@@ -49,6 +50,8 @@ using MPD::itDirectory;
using MPD::itSong;
using MPD::itPlaylist;
namespace fs = boost::filesystem;
Browser *myBrowser;
namespace {//
@@ -63,7 +66,7 @@ bool BrowserEntryMatcher(const boost::regex &rx, const MPD::Item &item, bool fil
Browser::Browser() : itsBrowseLocally(0), itsScrollBeginning(0), itsBrowsedDir("/")
{
w = NC::Menu<MPD::Item>(0, MainStartY, COLS, MainHeight, Config.columns_in_browser && Config.titles_visibility ? Display::Columns(COLS) : "", Config.main_color, NC::Border::None);
w = NC::Menu<MPD::Item>(0, MainStartY, COLS, MainHeight, Config.browser_display_mode == DisplayMode::Columns && Config.titles_visibility ? Display::Columns(COLS) : "", Config.main_color, NC::Border::None);
w.setHighlightColor(Config.main_highlight_color);
w.cyclicScrolling(Config.use_cyclic_scrolling);
w.centeredCursor(Config.centered_cursor);
@@ -78,7 +81,18 @@ void Browser::resize()
getWindowResizeParams(x_offset, width);
w.resize(width, MainHeight);
w.moveTo(x_offset, MainStartY);
w.setTitle(Config.columns_in_browser && Config.titles_visibility ? Display::Columns(w.getWidth()) : "");
switch (Config.browser_display_mode)
{
case DisplayMode::Columns:
if (Config.titles_visibility)
{
w.setTitle(Display::Columns(w.getWidth()));
break;
}
case DisplayMode::Classic:
w.setTitle("");
break;
}
hasToBeResized = 0;
}
@@ -97,7 +111,7 @@ void Browser::switchTo()
std::wstring Browser::title()
{
std::wstring result = L"Browse: ";
result += Scroller(ToWString(itsBrowsedDir), itsScrollBeginning, COLS-result.length()-(Config.new_design ? 2 : Global::VolumeState.length()));
result += Scroller(ToWString(itsBrowsedDir), itsScrollBeginning, COLS-result.length()-(Config.design == Design::Alternative ? 2 : Global::VolumeState.length()));
return result;
}
@@ -466,8 +480,6 @@ void Browser::GetDirectory(std::string dir, std::string subdir)
#ifndef WIN32
void Browser::GetLocalDirectory(MPD::ItemList &v, const std::string &directory, bool recursively) const
{
namespace fs = boost::filesystem;
size_t start_size = v.size();
fs::path dir(directory);
std::for_each(fs::directory_iterator(dir), fs::directory_iterator(), [&](fs::directory_entry &e) {
@@ -511,8 +523,6 @@ void Browser::GetLocalDirectory(MPD::ItemList &v, const std::string &directory,
void Browser::ClearDirectory(const std::string &path) const
{
namespace fs = boost::filesystem;
fs::path dir(path);
std::for_each(fs::directory_iterator(dir), fs::directory_iterator(), [&](fs::directory_entry &e) {
if (!fs::is_symlink(e) && fs::is_directory(e))
@@ -535,9 +545,15 @@ void Browser::ChangeBrowseMode()
Statusbar::printf("Browse mode: %1%",
itsBrowseLocally ? "Local filesystem" : "MPD database"
);
itsBrowsedDir = itsBrowseLocally ? Config.GetHomeDirectory() : "/";
if (itsBrowseLocally && *itsBrowsedDir.rbegin() == '/')
itsBrowsedDir.resize(itsBrowsedDir.length()-1);
if (itsBrowseLocally)
{
itsBrowsedDir = "~";
expand_home(itsBrowsedDir);
if (*itsBrowsedDir.rbegin() == '/')
itsBrowsedDir.resize(itsBrowsedDir.length()-1);
}
else
itsBrowsedDir = "/";
w.reset();
GetDirectory(itsBrowsedDir);
drawHeader();
@@ -605,10 +621,15 @@ std::string ItemToString(const MPD::Item &item)
result = "[" + getBasename(item.name) + "]";
break;
case MPD::itSong:
if (Config.columns_in_browser)
result = item.song->toString(Config.song_in_columns_to_string_format, Config.tags_separator);
else
result = item.song->toString(Config.song_list_format_dollar_free, Config.tags_separator);
switch (Config.browser_display_mode)
{
case DisplayMode::Classic:
result = item.song->toString(Config.song_list_format_dollar_free, Config.tags_separator);
break;
case DisplayMode::Columns:
result = item.song->toString(Config.song_in_columns_to_string_format, Config.tags_separator);
break;
}
break;
case MPD::itPlaylist:
result = Config.browser_playlist_prefix.str() + getBasename(item.name);

View File

@@ -18,11 +18,12 @@
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#include <boost/filesystem/operations.hpp>
#include <boost/program_options.hpp>
#include <iostream>
#include "bindings.h"
#include "cmdargs.h"
#include "configuration.h"
#include "config.h"
#include "mpdpp.h"
#include "settings.h"
@@ -36,25 +37,25 @@ namespace {
const char *env_home;
void replace_tilda_with_home(std::string &s)
}
void expand_home(std::string &path)
{
if (!s.empty() && s[0] == '~')
s.replace(0, 1, env_home);
if (!path.empty() && path[0] == '~')
path.replace(0, 1, env_home);
}
}
bool ParseArguments(int argc, char **argv)
bool configure(int argc, char **argv)
{
std::string bindings_path, config_path;
po::options_description desc("Options");
desc.add_options()
po::options_description options("Options");
options.add_options()
("host,h", po::value<std::string>()->default_value("localhost"), "connect to server at host")
("port,p", po::value<int>()->default_value(6600), "connect to server at port")
("config,c", po::value<std::string>(&config_path)->default_value("~/.ncmpcpp/config"), "specify configuration file")
("bindigs,b", po::value<std::string>(&bindings_path)->default_value("~/.ncmpcpp/bindings"), "specify bindings file")
("screen,s", po::value<std::string>(), "specify the startup screen")
("screen,s", po::value<std::string>(), "specify initial screen")
("help,?", "show help message")
("version,v", "display version information")
;
@@ -62,11 +63,11 @@ bool ParseArguments(int argc, char **argv)
po::variables_map vm;
try
{
po::store(po::parse_command_line(argc, argv, desc), vm);
po::store(po::parse_command_line(argc, argv, options), vm);
if (vm.count("help"))
{
cout << "Usage: " << argv[0] << " [options]...\n" << desc << "\n";
cout << "Usage: " << argv[0] << " [options]...\n" << options << "\n";
return false;
}
if (vm.count("version"))
@@ -119,30 +120,33 @@ bool ParseArguments(int argc, char **argv)
po::notify(vm);
// get home directory
env_home = getenv("HOME");
if (env_home == nullptr)
{
cerr << "Fatal error: HOME environment variable is not defined\n";
return false;
}
replace_tilda_with_home(config_path);
replace_tilda_with_home(bindings_path);
expand_home(config_path);
expand_home(bindings_path);
// read configuration
Config.SetDefaults();
Config.Read(config_path);
Config.GenerateColumns();
if (Config.read(config_path) == false)
exit(1);
// read bindings
if (Bindings.read(bindings_path) == false)
return false;
exit(1);
Bindings.generateDefaults();
// create directories
boost::filesystem::create_directory(Config.ncmpcpp_directory);
boost::filesystem::create_directory(Config.lyrics_directory);
// try to get MPD connection details from environment variables
// as they take precedence over these from the configuration.
auto env_host = getenv("MPD_HOST");
auto env_port = getenv("MPD_PORT");
if (env_host != nullptr)
Mpd.SetHostname(env_host);
if (env_port != nullptr)
@@ -150,10 +154,11 @@ bool ParseArguments(int argc, char **argv)
// if MPD connection details are provided as command line
// parameters, use them as their priority is the highest.
if (vm.count("host"))
if (!vm["host"].defaulted())
Mpd.SetHostname(vm["host"].as<std::string>());
if (vm.count("port"))
if (!vm["port"].defaulted())
Mpd.SetPort(vm["port"].as<int>());
Mpd.SetTimeout(Config.mpd_connection_timeout);
// custom startup screen
if (vm.count("screen"))
@@ -162,15 +167,15 @@ bool ParseArguments(int argc, char **argv)
Config.startup_screen_type = stringtoStartupScreenType(screen);
if (Config.startup_screen_type == ScreenType::Unknown)
{
std::cerr << "Invalid screen: " << screen << "\n";
return false;
std::cerr << "Unknown screen: " << screen << "\n";
exit(1);
}
}
}
catch (std::exception &e)
{
cerr << "Error while parsing command line options: " << e.what() << "\n";
return false;
cerr << "Error while processing configuration: " << e.what() << "\n";
exit(1);
}
return true;
}

View File

@@ -18,9 +18,11 @@
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#ifndef NCMPCPP_CMDARGS_H
#define NCMPCPP_CMDARGS_H
#ifndef NCMPCPP_CONFIGURATION_H
#define NCMPCPP_CONFIGURATION_H
bool ParseArguments(int argc, char **argv);
void expand_home(std::string &path);
#endif // NCMPCPP_CMDARGS_H
bool configure(int argc, char **argv);
#endif // NCMPCPP_CONFIGURATION_H

View File

@@ -392,10 +392,15 @@ void Display::Items(NC::Menu<MPD::Item> &menu, const ProxySongList &pl)
<< "]";
break;
case MPD::itSong:
if (!Config.columns_in_browser)
showSongs(menu, *item.song, pl, Config.song_list_format);
else
showSongsInColumns(menu, *item.song, pl);
switch (Config.browser_display_mode)
{
case DisplayMode::Classic:
showSongs(menu, *item.song, pl, Config.song_list_format);
break;
case DisplayMode::Columns:
showSongsInColumns(menu, *item.song, pl);
break;
}
break;
case MPD::itPlaylist:
menu << Config.browser_playlist_prefix
@@ -409,10 +414,15 @@ void Display::SEItems(NC::Menu<SEItem> &menu, const ProxySongList &pl)
const SEItem &si = menu.drawn()->value();
if (si.isSong())
{
if (!Config.columns_in_search_engine)
showSongs(menu, si.song(), pl, Config.song_list_format);
else
showSongsInColumns(menu, si.song(), pl);
switch (Config.search_engine_display_mode)
{
case DisplayMode::Classic:
showSongs(menu, si.song(), pl, Config.song_list_format);
break;
case DisplayMode::Columns:
showSongsInColumns(menu, si.song(), pl);
break;
}
}
else
menu << si.buffer();

134
src/enums.cpp Normal file
View File

@@ -0,0 +1,134 @@
/***************************************************************************
* 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 "enums.h"
std::ostream &operator<<(std::ostream &os, SpaceAddMode sam)
{
switch (sam)
{
case SpaceAddMode::AddRemove:
os << "add_remove";
break;
case SpaceAddMode::AlwaysAdd:
os << "always_add";
break;
}
return os;
}
std::istream &operator>>(std::istream &is, SpaceAddMode &sam)
{
std::string ssam;
is >> ssam;
if (ssam == "add_remove")
sam = SpaceAddMode::AddRemove;
else if (ssam == "always_add")
sam = SpaceAddMode::AlwaysAdd;
else
is.setstate(std::ios::failbit);
return is;
}
std::ostream &operator<<(std::ostream &os, SortMode sm)
{
switch (sm)
{
case SortMode::Name:
os << "name";
break;
case SortMode::ModificationTime:
os << "mtime";
break;
case SortMode::CustomFormat:
os << "format";
break;
}
return os;
}
std::istream &operator>>(std::istream &is, SortMode &sm)
{
std::string ssm;
is >> ssm;
if (ssm == "name")
sm = SortMode::Name;
else if (ssm == "mtime")
sm = SortMode::ModificationTime;
else if (ssm == "format")
sm = SortMode::CustomFormat;
else
is.setstate(std::ios::failbit);
return is;
}
std::ostream &operator<<(std::ostream &os, DisplayMode dm)
{
switch (dm)
{
case DisplayMode::Classic:
os << "classic";
break;
case DisplayMode::Columns:
os << "columns";
break;
}
return os;
}
std::istream &operator>>(std::istream &is, DisplayMode &dm)
{
std::string sdm;
is >> sdm;
if (sdm == "classic")
dm = DisplayMode::Classic;
else if (sdm == "columns")
dm = DisplayMode::Columns;
else
is.setstate(std::ios::failbit);
return is;
}
std::ostream &operator<<(std::ostream &os, Design ui)
{
switch (ui)
{
case Design::Classic:
os << "classic";
break;
case Design::Alternative:
os << "alternative";
break;
}
return os;
}
std::istream &operator>>(std::istream &is, Design &ui)
{
std::string sui;
is >> sui;
if (sui == "classic")
ui = Design::Classic;
else if (sui == "alternative")
ui = Design::Alternative;
else
is.setstate(std::ios::failbit);
return is;
}

42
src/enums.h Normal file
View File

@@ -0,0 +1,42 @@
/***************************************************************************
* 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_ENUMS_H
#define NCMPCPP_ENUMS_H
#include <iostream>
enum class SpaceAddMode { AddRemove, AlwaysAdd };
std::ostream &operator<<(std::ostream &os, SpaceAddMode sam);
std::istream &operator>>(std::istream &is, SpaceAddMode &sam);
enum class SortMode { Name, ModificationTime, CustomFormat };
std::ostream &operator<<(std::ostream &os, SortMode sm);
std::istream &operator>>(std::istream &is, SortMode &sm);
enum class DisplayMode { Classic, Columns };
std::ostream &operator<<(std::ostream &os, DisplayMode dm);
std::istream &operator>>(std::istream &is, DisplayMode &dm);
enum class Design { Classic, Alternative };
std::ostream &operator<<(std::ostream &os, Design ui);
std::istream &operator>>(std::istream &is, Design &ui);
#endif // NCMPCPP_ENUMS_H

View File

@@ -27,7 +27,7 @@
bool addSongToPlaylist(const MPD::Song &s, bool play, int position)
{
bool result = false;
if (Config.ncmpc_like_songs_adding && myPlaylist->checkForSong(s))
if (Config.space_add_mode == SpaceAddMode::AddRemove && myPlaylist->checkForSong(s))
{
auto &w = myPlaylist->main();
if (play)

View File

@@ -434,6 +434,14 @@ void stringToBuffer(const std::basic_string<CharT> &s, NC::BasicBuffer<CharT> &b
stringToBuffer(s.begin(), s.end(), buf);
}
template <typename CharT>
NC::BasicBuffer<CharT> stringToBuffer(const std::basic_string<CharT> &s)
{
NC::BasicBuffer<CharT> result;
stringToBuffer(s, result);
return result;
}
template <typename T> void ShowTime(T &buf, size_t length, bool short_names)
{
const unsigned MINUTE = 60;

View File

@@ -133,7 +133,7 @@ void Lyrics::switchTo()
std::wstring Lyrics::title()
{
std::wstring result = L"Lyrics: ";
result += Scroller(ToWString(itsSong.toString("{%a - %t}", ", ")), itsScrollBegin, COLS-result.length()-(Config.new_design ? 2 : Global::VolumeState.length()));
result += Scroller(ToWString(itsSong.toString("{%a - %t}", ", ")), itsScrollBegin, COLS-result.length()-(Config.design == Design::Alternative ? 2 : Global::VolumeState.length()));
return result;
}
@@ -314,8 +314,6 @@ void Lyrics::Load()
itsFilename = GenerateFilename(itsSong);
CreateDir(Config.lyrics_directory);
w.clear();
w.reset();

View File

@@ -34,7 +34,7 @@
#include "bindings.h"
#include "browser.h"
#include "charset.h"
#include "cmdargs.h"
#include "configuration.h"
#include "global.h"
#include "error.h"
#include "helpers.h"
@@ -101,12 +101,9 @@ int main(int argc, char **argv)
std::setlocale(LC_ALL, "");
std::locale::global(Charset::internalLocale());
if (!ParseArguments(argc, argv))
if (!configure(argc, argv))
return 0;
Mpd.SetTimeout(Config.mpd_connection_timeout);
CreateDir(Config.ncmpcpp_directory);
// always execute these commands, even if ncmpcpp use exit function
atexit(do_at_exit);
@@ -122,7 +119,7 @@ int main(int argc, char **argv)
if (!Config.titles_visibility)
wattron(stdscr, COLOR_PAIR(int(Config.main_color)));
if (Config.new_design)
if (Config.design == Design::Alternative)
Config.statusbar_visibility = 0;
Actions::setWindowsDimensions();
@@ -130,7 +127,7 @@ int main(int argc, char **argv)
Actions::initializeScreens();
wHeader = new NC::Window(0, 0, COLS, Actions::HeaderHeight, "", Config.header_color, NC::Border::None);
if (Config.header_visibility || Config.new_design)
if (Config.header_visibility || Config.design == Design::Alternative)
wHeader->display();
wFooter = new NC::Window(0, Actions::FooterStartY, COLS, Actions::FooterHeight, "", Config.statusbar_color, NC::Border::None);
@@ -140,14 +137,14 @@ int main(int argc, char **argv)
// initialize global timer
Timer = boost::posix_time::microsec_clock::local_time();
// go to playlist
// initialize playlist
myPlaylist->switchTo();
// local variables
Key input(0, Key::Standard);
boost::posix_time::ptime past = boost::posix_time::from_time_t(0);
// local variables end
auto past = boost::posix_time::from_time_t(0);
/// enable mouse
mouseinterval(0);
if (Config.mouse_support)
mousemask(ALL_MOUSE_EVENTS, 0);
@@ -240,7 +237,7 @@ int main(int argc, char **argv)
}
catch (OutOfBounds &e)
{
Statusbar::print(e.errorMessage());
Statusbar::printf("Error: %1%", e.errorMessage());
}
if (myScreen == myPlaylist)

View File

@@ -53,16 +53,21 @@ bool playlistEntryMatcher(const boost::regex &rx, const MPD::Song &s);
Playlist::Playlist()
: itsTotalLength(0), itsRemainingTime(0), itsScrollBegin(0), m_old_playlist_version(0)
{
w = NC::Menu<MPD::Song>(0, MainStartY, COLS, MainHeight, Config.columns_in_playlist && Config.titles_visibility ? Display::Columns(COLS) : "", Config.main_color, NC::Border::None);
w = NC::Menu<MPD::Song>(0, MainStartY, COLS, MainHeight, Config.playlist_display_mode == DisplayMode::Columns && Config.titles_visibility ? Display::Columns(COLS) : "", Config.main_color, NC::Border::None);
w.cyclicScrolling(Config.use_cyclic_scrolling);
w.centeredCursor(Config.centered_cursor);
w.setHighlightColor(Config.main_highlight_color);
w.setSelectedPrefix(Config.selected_item_prefix);
w.setSelectedSuffix(Config.selected_item_suffix);
if (Config.columns_in_playlist)
w.setItemDisplayer(boost::bind(Display::SongsInColumns, _1, proxySongList()));
else
w.setItemDisplayer(boost::bind(Display::Songs, _1, proxySongList(), Config.song_list_format));
switch (Config.playlist_display_mode)
{
case DisplayMode::Classic:
w.setItemDisplayer(boost::bind(Display::Songs, _1, proxySongList(), Config.song_list_format));
break;
case DisplayMode::Columns:
w.setItemDisplayer(boost::bind(Display::SongsInColumns, _1, proxySongList()));
break;
}
}
void Playlist::switchTo()
@@ -80,11 +85,18 @@ void Playlist::resize()
w.resize(width, MainHeight);
w.moveTo(x_offset, MainStartY);
if (Config.columns_in_playlist && Config.titles_visibility)
w.setTitle(Display::Columns(w.getWidth()));
else
w.setTitle("");
switch (Config.playlist_display_mode)
{
case DisplayMode::Columns:
if (Config.titles_visibility)
{
w.setTitle(Display::Columns(w.getWidth()));
break;
}
case DisplayMode::Classic:
w.setTitle("");
}
hasToBeResized = 0;
}
@@ -93,7 +105,7 @@ std::wstring Playlist::title()
std::wstring result = L"Playlist ";
if (ReloadTotalLength || ReloadRemaining)
itsBufferedStats = TotalLength();
result += Scroller(ToWString(itsBufferedStats), itsScrollBegin, COLS-result.length()-(Config.new_design ? 2 : Global::VolumeState.length()));
result += Scroller(ToWString(itsBufferedStats), itsScrollBegin, COLS-result.length()-(Config.design == Design::Alternative ? 2 : Global::VolumeState.length()));
return result;
}
@@ -373,10 +385,14 @@ namespace {//
std::string songToString(const MPD::Song &s)
{
std::string result;
if (Config.columns_in_playlist)
result = s.toString(Config.song_in_columns_to_string_format, Config.tags_separator);
else
result = s.toString(Config.song_list_format_dollar_free, Config.tags_separator);
switch (Config.playlist_display_mode)
{
case DisplayMode::Classic:
result = s.toString(Config.song_list_format_dollar_free, Config.tags_separator);
break;
case DisplayMode::Columns:
result = s.toString(Config.song_in_columns_to_string_format, Config.tags_separator);
}
return result;
}

View File

@@ -77,10 +77,15 @@ PlaylistEditor::PlaylistEditor()
Content.centeredCursor(Config.centered_cursor);
Content.setSelectedPrefix(Config.selected_item_prefix);
Content.setSelectedSuffix(Config.selected_item_suffix);
if (Config.columns_in_playlist_editor)
Content.setItemDisplayer(boost::bind(Display::SongsInColumns, _1, contentProxyList()));
else
Content.setItemDisplayer(boost::bind(Display::Songs, _1, contentProxyList(), Config.song_list_format));
switch (Config.playlist_editor_display_mode)
{
case DisplayMode::Classic:
Content.setItemDisplayer(boost::bind(Display::Songs, _1, contentProxyList(), Config.song_list_format));
break;
case DisplayMode::Columns:
Content.setItemDisplayer(boost::bind(Display::SongsInColumns, _1, contentProxyList()));
break;
}
w = &Playlists;
}
@@ -550,10 +555,15 @@ namespace {//
std::string SongToString(const MPD::Song &s)
{
std::string result;
if (Config.columns_in_playlist_editor)
result = s.toString(Config.song_in_columns_to_string_format, Config.tags_separator);
else
result = s.toString(Config.song_list_format_dollar_free, Config.tags_separator);
switch (Config.playlist_display_mode)
{
case DisplayMode::Classic:
result = s.toString(Config.song_list_format_dollar_free, Config.tags_separator);
break;
case DisplayMode::Columns:
result = s.toString(Config.song_in_columns_to_string_format, Config.tags_separator);
break;
}
return result;
}

View File

@@ -112,8 +112,10 @@ BaseScreen *toScreen(ScreenType st)
# endif // ENABLE_CLOCK
case ScreenType::Help:
return myHelp;
# ifdef HAVE_CURL_CURL_H
case ScreenType::Lastfm:
return myLastfm;
# endif // HAVE_CURL_CURL_H
case ScreenType::Lyrics:
return myLyrics;
case ScreenType::MediaLibrary:

View File

@@ -119,7 +119,17 @@ void SearchEngine::resize()
getWindowResizeParams(x_offset, width);
w.resize(width, MainHeight);
w.moveTo(x_offset, MainStartY);
w.setTitle(Config.columns_in_search_engine && Config.titles_visibility ? Display::Columns(w.getWidth()) : "");
switch (Config.search_engine_display_mode)
{
case DisplayMode::Columns:
if (Config.titles_visibility)
{
w.setTitle(Display::Columns(w.getWidth()));
break;
}
case DisplayMode::Classic:
w.setTitle("");
}
hasToBeResized = 0;
}
@@ -175,7 +185,7 @@ void SearchEngine::enterPressed()
Search();
if (w.back().value().isSong())
{
if (Config.columns_in_search_engine)
if (Config.search_engine_display_mode == DisplayMode::Columns)
w.setTitle(Config.titles_visibility ? Display::Columns(w.getWidth()) : "");
size_t found = w.size()-SearchEngine::StaticOptions;
found += 3; // don't count options inserted below
@@ -616,10 +626,15 @@ std::string SEItemToString(const SEItem &ei)
std::string result;
if (ei.isSong())
{
if (Config.columns_in_search_engine)
result = ei.song().toString(Config.song_in_columns_to_string_format, Config.tags_separator);
else
result = ei.song().toString(Config.song_list_format_dollar_free, Config.tags_separator);
switch (Config.search_engine_display_mode)
{
case DisplayMode::Classic:
result = ei.song().toString(Config.song_list_format_dollar_free, Config.tags_separator);
break;
case DisplayMode::Columns:
result = ei.song().toString(Config.song_in_columns_to_string_format, Config.tags_separator);
break;
}
}
else
result = ei.buffer().str();

File diff suppressed because it is too large Load Diff

View File

@@ -26,12 +26,11 @@
#include <cassert>
#include <vector>
#include <mpd/client.h>
#include "actions.h"
#include "enums.h"
#include "screen_type.h"
#include "strbuffer.h"
enum SortMode { smName, smMTime, smCustomFormat };
struct Column
{
Column() : stretch_limit(-1), right_alignment(0), display_empty_tag(1) { }
@@ -48,25 +47,20 @@ struct Column
struct Configuration
{
Configuration();
Configuration()
: playlist_disable_highlight_delay(0), visualizer_sync_interval(0)
{ }
const std::string &GetHomeDirectory();
void CheckForCommandLineConfigFilePath(char **argv, int argc);
void SetDefaults();
void Read(const std::string& config_path);
void GenerateColumns();
bool read(const std::string &config_path);
std::string ncmpcpp_directory;
std::string lyrics_directory;
std::string mpd_host;
std::string mpd_music_dir;
std::string visualizer_fifo_path;
std::string visualizer_output_name;
std::string empty_tag;
std::string tags_separator;
std::string song_list_columns_format;
std::string song_list_format;
std::string song_list_format_dollar_free;
std::string song_status_format;
@@ -86,9 +80,14 @@ struct Configuration
std::wstring visualizer_chars;
std::string pattern;
std::vector<Column> columns;
DisplayMode playlist_display_mode;
DisplayMode browser_display_mode;
DisplayMode search_engine_display_mode;
DisplayMode playlist_editor_display_mode;
NC::Buffer browser_playlist_prefix;
NC::Buffer selected_item_prefix;
NC::Buffer selected_item_suffix;
@@ -115,16 +114,16 @@ struct Configuration
NC::Border window_border;
NC::Border active_window_border;
Design design;
SpaceAddMode space_add_mode;
mpd_tag_type media_lib_primary_tag;
bool colors_enabled;
bool playlist_show_remaining_time;
bool playlist_shorten_total_times;
bool playlist_separate_albums;
bool columns_in_playlist;
bool columns_in_browser;
bool columns_in_search_engine;
bool columns_in_playlist_editor;
bool set_window_title;
bool header_visibility;
bool header_text_scrolling;
@@ -135,7 +134,6 @@ struct Configuration
bool autocenter_mode;
bool wrapped_search;
bool space_selects;
bool ncmpc_like_songs_adding;
bool incremental_seeking;
bool now_playing_lyrics;
bool fetch_lyrics_in_background;
@@ -150,10 +148,9 @@ struct Configuration
bool block_search_constraints_change;
bool use_console_editor;
bool use_cyclic_scrolling;
bool ask_before_clearing_main_playlist;
bool ask_before_clearing_playlists;
bool mouse_support;
bool mouse_list_scroll_whole_page;
bool new_design;
bool visualizer_use_wave;
bool visualizer_in_stereo;
bool media_library_sort_by_mtime;
@@ -165,18 +162,16 @@ struct Configuration
bool allow_for_physical_item_deletion;
bool progressbar_boldness;
int mpd_port;
int mpd_connection_timeout;
int crossfade_time;
int seek_time;
int volume_change_step;
int message_delay_time;
int lyrics_db;
boost::regex::flag_type regex_type;
unsigned mpd_connection_timeout;
unsigned crossfade_time;
unsigned seek_time;
unsigned volume_change_step;
unsigned message_delay_time;
unsigned lyrics_db;
unsigned lines_scrolled;
unsigned search_engine_default_search_mode;
boost::regex::flag_type regex_type;
boost::posix_time::seconds playlist_disable_highlight_delay;
boost::posix_time::seconds visualizer_sync_interval;
@@ -189,19 +184,12 @@ struct Configuration
size_t now_playing_suffix_length;
ScreenType startup_screen_type;
std::list<ScreenType> screens_seq;
std::list<ScreenType> screen_sequence;
SortMode browser_sort_mode;
private:
void MakeProperPath(std::string &dir);
std::string home_directory;
};
extern Configuration Config;
void CreateDir(const std::string &dir);
#endif // NCMPCPP_SETTINGS_H

View File

@@ -288,7 +288,7 @@ std::string Song::ShowTime(unsigned length)
return result;
}
bool MPD::Song::isFormatOk(const std::string &type, const std::string &fmt)
void MPD::Song::validateFormat(const std::string &fmt)
{
int braces = 0;
for (std::string::const_iterator it = fmt.begin(); it != fmt.end(); ++it)
@@ -299,22 +299,17 @@ bool MPD::Song::isFormatOk(const std::string &type, const std::string &fmt)
--braces;
}
if (braces)
{
std::cerr << type << ": number of opening and closing braces does not equal\n";
return false;
}
throw std::runtime_error("number of opening and closing braces is not equal");
for (size_t i = fmt.find('%'); i != std::string::npos; i = fmt.find('%', i))
{
if (isdigit(fmt[++i]))
while (isdigit(fmt[++i])) { }
if (!charToGetFunction(fmt[i]))
{
std::cerr << type << ": invalid character at position " << boost::lexical_cast<std::string>(i+1) << ": '" << fmt[i] << "'\n";
return false;
}
throw std::runtime_error(
(boost::format("invalid character at position %1%: %2%") % (i+1) % fmt[i]).str()
);
}
return true;
}
std::string Song::ParseFormat(std::string::const_iterator &it, const std::string &tags_separator,

View File

@@ -80,7 +80,7 @@ struct Song
bool operator!=(const Song &rhs) const { return m_hash != rhs.m_hash; }
static std::string ShowTime(unsigned length);
static bool isFormatOk(const std::string &type, const std::string &fmt);
static void validateFormat(const std::string &fmt);
static const char FormatEscapeCharacter = 1;

View File

@@ -80,19 +80,19 @@ std::string playerStateToString(MPD::PlayerState ps)
result = "[unknown]";
break;
case MPD::psPlay:
if (Config.new_design)
if (Config.design == Design::Alternative)
result = "[playing]";
else
result = "Playing: ";
break;
case MPD::psPause:
if (Config.new_design)
if (Config.design == Design::Alternative)
result = "[paused] ";
else
result = "[Paused] ";
break;
case MPD::psStop:
if (Config.new_design)
if (Config.design == Design::Alternative)
result = "[stopped]";
break;
default:
@@ -337,7 +337,7 @@ void Status::Changes::playerState()
if (Progressbar::isUnlocked())
Progressbar::draw(0, 0);
Playlist::ReloadRemaining = true;
if (Config.new_design)
if (Config.design == Design::Alternative)
{
*wHeader << NC::XY(0, 0) << wclrtoeol << NC::XY(0, 1) << wclrtoeol;
mixer();
@@ -358,7 +358,7 @@ void Status::Changes::playerState()
# endif // ENABLE_VISUALIZER
std::string state = playerStateToString(State::player());
if (Config.new_design)
if (Config.design == Design::Alternative)
{
*wHeader << NC::XY(0, 1) << NC::Format::Bold << state << NC::Format::NoBold;
wHeader->refresh();
@@ -428,85 +428,88 @@ void Status::Changes::elapsedTime(bool update_elapsed)
drawTitle(np);
std::string tracklength;
if (Config.new_design)
switch (Config.design)
{
if (Config.display_remaining_time)
{
tracklength = "-";
tracklength += MPD::Song::ShowTime(st.totalTime()-st.elapsedTime());
}
else
tracklength = MPD::Song::ShowTime(st.elapsedTime());
if (st.totalTime())
{
tracklength += "/";
tracklength += MPD::Song::ShowTime(st.totalTime());
}
// bitrate here doesn't look good, but it can be moved somewhere else later
if (Config.display_bitrate && st.kbps())
{
tracklength += " ";
tracklength += boost::lexical_cast<std::string>(st.kbps());
tracklength += " kbps";
}
NC::WBuffer first, second;
stringToBuffer(ToWString(Charset::utf8ToLocale(np.toString(Config.new_header_first_line, Config.tags_separator, "$"))), first);
stringToBuffer(ToWString(Charset::utf8ToLocale(np.toString(Config.new_header_second_line, Config.tags_separator, "$"))), second);
size_t first_len = wideLength(first.str());
size_t first_margin = (std::max(tracklength.length()+1, VolumeState.length()))*2;
size_t first_start = first_len < COLS-first_margin ? (COLS-first_len)/2 : tracklength.length()+1;
size_t second_len = wideLength(second.str());
size_t second_margin = (std::max(ps.length(), size_t(8))+1)*2;
size_t second_start = second_len < COLS-second_margin ? (COLS-second_len)/2 : ps.length()+1;
if (!Global::SeekingInProgress)
*wHeader << NC::XY(0, 0) << wclrtoeol << tracklength;
*wHeader << NC::XY(first_start, 0);
writeCyclicBuffer(first, *wHeader, first_line_scroll_begin, COLS-tracklength.length()-VolumeState.length()-1, L" ** ");
*wHeader << NC::XY(0, 1) << wclrtoeol << NC::Format::Bold << ps << NC::Format::NoBold;
*wHeader << NC::XY(second_start, 1);
writeCyclicBuffer(second, *wHeader, second_line_scroll_begin, COLS-ps.length()-8-2, L" ** ");
*wHeader << NC::XY(wHeader->getWidth()-VolumeState.length(), 0) << Config.volume_color << VolumeState << NC::Color::End;
flags();
}
else if (Statusbar::isUnlocked() && Config.statusbar_visibility)
{
if (Config.display_bitrate && st.kbps())
{
tracklength += " [";
tracklength += boost::lexical_cast<std::string>(st.kbps());
tracklength += " kbps]";
}
tracklength += " [";
if (st.totalTime())
{
case Design::Classic:
if (Statusbar::isUnlocked() && Config.statusbar_visibility)
{
if (Config.display_bitrate && st.kbps())
{
tracklength += " [";
tracklength += boost::lexical_cast<std::string>(st.kbps());
tracklength += " kbps]";
}
tracklength += " [";
if (st.totalTime())
{
if (Config.display_remaining_time)
{
tracklength += "-";
tracklength += MPD::Song::ShowTime(st.totalTime()-st.elapsedTime());
}
else
tracklength += MPD::Song::ShowTime(st.elapsedTime());
tracklength += "/";
tracklength += MPD::Song::ShowTime(st.totalTime());
tracklength += "]";
}
else
{
tracklength += MPD::Song::ShowTime(st.elapsedTime());
tracklength += "]";
}
NC::WBuffer np_song;
stringToBuffer(ToWString(Charset::utf8ToLocale(np.toString(Config.song_status_format, Config.tags_separator, "$"))), np_song);
*wFooter << NC::XY(0, 1) << wclrtoeol << NC::Format::Bold << ps << NC::Format::NoBold;
writeCyclicBuffer(np_song, *wFooter, playing_song_scroll_begin, wFooter->getWidth()-ps.length()-tracklength.length(), L" ** ");
*wFooter << NC::Format::Bold << NC::XY(wFooter->getWidth()-tracklength.length(), 1) << tracklength << NC::Format::NoBold;
}
break;
case Design::Alternative:
if (Config.display_remaining_time)
{
tracklength += "-";
tracklength = "-";
tracklength += MPD::Song::ShowTime(st.totalTime()-st.elapsedTime());
}
else
tracklength += MPD::Song::ShowTime(st.elapsedTime());
tracklength += "/";
tracklength += MPD::Song::ShowTime(st.totalTime());
tracklength += "]";
}
else
{
tracklength += MPD::Song::ShowTime(st.elapsedTime());
tracklength += "]";
}
NC::WBuffer np_song;
stringToBuffer(ToWString(Charset::utf8ToLocale(np.toString(Config.song_status_format, Config.tags_separator, "$"))), np_song);
*wFooter << NC::XY(0, 1) << wclrtoeol << NC::Format::Bold << ps << NC::Format::NoBold;
writeCyclicBuffer(np_song, *wFooter, playing_song_scroll_begin, wFooter->getWidth()-ps.length()-tracklength.length(), L" ** ");
*wFooter << NC::Format::Bold << NC::XY(wFooter->getWidth()-tracklength.length(), 1) << tracklength << NC::Format::NoBold;
tracklength = MPD::Song::ShowTime(st.elapsedTime());
if (st.totalTime())
{
tracklength += "/";
tracklength += MPD::Song::ShowTime(st.totalTime());
}
// bitrate here doesn't look good, but it can be moved somewhere else later
if (Config.display_bitrate && st.kbps())
{
tracklength += " ";
tracklength += boost::lexical_cast<std::string>(st.kbps());
tracklength += " kbps";
}
NC::WBuffer first, second;
stringToBuffer(ToWString(Charset::utf8ToLocale(np.toString(Config.new_header_first_line, Config.tags_separator, "$"))), first);
stringToBuffer(ToWString(Charset::utf8ToLocale(np.toString(Config.new_header_second_line, Config.tags_separator, "$"))), second);
size_t first_len = wideLength(first.str());
size_t first_margin = (std::max(tracklength.length()+1, VolumeState.length()))*2;
size_t first_start = first_len < COLS-first_margin ? (COLS-first_len)/2 : tracklength.length()+1;
size_t second_len = wideLength(second.str());
size_t second_margin = (std::max(ps.length(), size_t(8))+1)*2;
size_t second_start = second_len < COLS-second_margin ? (COLS-second_len)/2 : ps.length()+1;
if (!Global::SeekingInProgress)
*wHeader << NC::XY(0, 0) << wclrtoeol << tracklength;
*wHeader << NC::XY(first_start, 0);
writeCyclicBuffer(first, *wHeader, first_line_scroll_begin, COLS-tracklength.length()-VolumeState.length()-1, L" ** ");
*wHeader << NC::XY(0, 1) << wclrtoeol << NC::Format::Bold << ps << NC::Format::NoBold;
*wHeader << NC::XY(second_start, 1);
writeCyclicBuffer(second, *wHeader, second_line_scroll_begin, COLS-ps.length()-8-2, L" ** ");
*wHeader << NC::XY(wHeader->getWidth()-VolumeState.length(), 0) << Config.volume_color << VolumeState << NC::Color::End;
flags();
}
if (Progressbar::isUnlocked())
Progressbar::draw(st.elapsedTime(), st.totalTime());
@@ -557,69 +560,78 @@ void Status::Changes::dbUpdateState(bool show_msg)
void Status::Changes::flags()
{
if (!Config.header_visibility && !Config.new_design)
if (!Config.header_visibility && Config.design == Design::Classic)
return;
std::string switch_state;
if (Config.new_design)
switch (Config.design)
{
switch_state += '[';
switch_state += m_repeat ? m_repeat : '-';
switch_state += m_random ? m_random : '-';
switch_state += m_single ? m_single : '-';
switch_state += m_consume ? m_consume : '-';
switch_state += m_crossfade ? m_crossfade : '-';
switch_state += m_db_updating ? m_db_updating : '-';
switch_state += ']';
*wHeader << NC::XY(COLS-switch_state.length(), 1) << NC::Format::Bold << Config.state_flags_color << switch_state << NC::Color::End << NC::Format::NoBold;
if (Config.new_design && !Config.header_visibility) // in this case also draw separator
{
*wHeader << NC::Format::Bold << NC::Color::Black;
mvwhline(wHeader->raw(), 2, 0, 0, COLS);
*wHeader << NC::Color::End << NC::Format::NoBold;
}
wHeader->refresh();
}
else
{
if (m_repeat)
switch_state += m_repeat;
if (m_random)
switch_state += m_random;
if (m_single)
switch_state += m_single;
if (m_consume)
switch_state += m_consume;
if (m_crossfade)
switch_state += m_crossfade;
if (m_db_updating)
switch_state += m_db_updating;
// this is done by raw ncurses because creating another
// window only for handling this is quite silly
attrset(A_BOLD|COLOR_PAIR(int(Config.state_line_color)));
mvhline(1, 0, 0, COLS);
if (!switch_state.empty())
{
mvprintw(1, COLS-switch_state.length()-3, "[");
attroff(COLOR_PAIR(int(Config.state_line_color)));
attron(COLOR_PAIR(int(Config.state_flags_color)));
mvprintw(1, COLS-switch_state.length()-2, "%s", switch_state.c_str());
attroff(COLOR_PAIR(int(Config.state_flags_color)));
attron(COLOR_PAIR(int(Config.state_line_color)));
mvprintw(1, COLS-2, "]");
}
attroff(A_BOLD|COLOR_PAIR(int(Config.state_line_color)));
refresh();
case Design::Classic:
if (m_repeat)
switch_state += m_repeat;
if (m_random)
switch_state += m_random;
if (m_single)
switch_state += m_single;
if (m_consume)
switch_state += m_consume;
if (m_crossfade)
switch_state += m_crossfade;
if (m_db_updating)
switch_state += m_db_updating;
// this is done by raw ncurses because creating another
// window only for handling this is quite silly
attrset(A_BOLD|COLOR_PAIR(int(Config.state_line_color)));
mvhline(1, 0, 0, COLS);
if (!switch_state.empty())
{
mvprintw(1, COLS-switch_state.length()-3, "[");
attroff(COLOR_PAIR(int(Config.state_line_color)));
attron(COLOR_PAIR(int(Config.state_flags_color)));
mvprintw(1, COLS-switch_state.length()-2, "%s", switch_state.c_str());
attroff(COLOR_PAIR(int(Config.state_flags_color)));
attron(COLOR_PAIR(int(Config.state_line_color)));
mvprintw(1, COLS-2, "]");
}
attroff(A_BOLD|COLOR_PAIR(int(Config.state_line_color)));
refresh();
break;
case Design::Alternative:
switch_state += '[';
switch_state += m_repeat ? m_repeat : '-';
switch_state += m_random ? m_random : '-';
switch_state += m_single ? m_single : '-';
switch_state += m_consume ? m_consume : '-';
switch_state += m_crossfade ? m_crossfade : '-';
switch_state += m_db_updating ? m_db_updating : '-';
switch_state += ']';
*wHeader << NC::XY(COLS-switch_state.length(), 1) << NC::Format::Bold << Config.state_flags_color << switch_state << NC::Color::End << NC::Format::NoBold;
if (!Config.header_visibility) // in this case also draw separator
{
*wHeader << NC::Format::Bold << NC::Color::Black;
mvwhline(wHeader->raw(), 2, 0, 0, COLS);
*wHeader << NC::Color::End << NC::Format::NoBold;
}
wHeader->refresh();
break;
}
}
void Status::Changes::mixer()
{
if (!Config.display_volume_level || (!Config.header_visibility && !Config.new_design))
if (!Config.display_volume_level || (!Config.header_visibility && Config.design == Design::Classic))
return;
VolumeState = Config.new_design ? " Vol: " : " Volume: ";
switch (Config.design)
{
case Design::Classic:
VolumeState = " " "Volume" ": ";
break;
case Design::Alternative:
VolumeState = " " "Vol" ": ";
break;
}
if (State::volume() < 0)
VolumeState += "n/a";
else

View File

@@ -106,10 +106,15 @@ void Statusbar::unlock()
}
if (Status::State::player() == MPD::psStop)
{
if (Config.new_design)
Progressbar::draw(Status::State::elapsedTime(), myPlaylist->currentSongLength());
else
put() << wclrtoeol;
switch (Config.design)
{
case Design::Classic:
put() << wclrtoeol;
break;
case Design::Alternative:
Progressbar::draw(Status::State::elapsedTime(), myPlaylist->currentSongLength());
break;
}
wFooter->refresh();
}
}
@@ -134,10 +139,15 @@ void Statusbar::tryRedraw()
if (Status::State::player() != MPD::psStop && !statusbarBlockUpdate && !progressbarBlockUpdate)
{
if (Config.new_design)
Progressbar::draw(Status::State::elapsedTime(), myPlaylist->currentSongLength());
else
Status::Changes::elapsedTime(false);
switch (Config.design)
{
case Design::Classic:
Status::Changes::elapsedTime(false);
break;
case Design::Alternative:
Progressbar::draw(Status::State::elapsedTime(), myPlaylist->currentSongLength());
break;
}
wFooter->refresh();
}
}

View File

@@ -63,18 +63,19 @@ template <typename CharT> class BasicBuffer
return m_id < rhs.m_id;
}
friend Window &operator<<(Window &w, const Property &p)
template <typename OutputStreamT>
friend OutputStreamT &operator<<(OutputStreamT &os, const Property &p)
{
switch (p.m_type)
{
case Type::Color:
w << p.m_color;
os << p.m_color;
break;
case Type::Format:
w << p.m_format;
os << p.m_format;
break;
}
return w;
return os;
}
private:
@@ -89,8 +90,12 @@ public:
typedef std::basic_string<CharT> StringType;
typedef std::set<Property> Properties;
BasicBuffer() { }
template <typename... Args>
BasicBuffer(Args... args)
{
construct(std::forward<Args>(args)...);
}
const StringType &str() const { return m_string; }
const Properties &properties() const { return m_properties; }
@@ -183,6 +188,14 @@ public:
}
private:
void construct() { }
template <typename ArgT, typename... Args>
void construct(ArgT &&arg, Args... args)
{
*this << std::forward<ArgT>(arg);
construct(std::forward<Args>(args)...);
}
StringType m_string;
Properties m_properties;
};
@@ -190,11 +203,11 @@ private:
typedef BasicBuffer<char> Buffer;
typedef BasicBuffer<wchar_t> WBuffer;
template <typename CharT>
Window &operator<<(Window &w, const BasicBuffer<CharT> &buffer)
template <typename OutputStreamT, typename CharT>
OutputStreamT &operator<<(OutputStreamT &os, const BasicBuffer<CharT> &buffer)
{
if (buffer.properties().empty())
w << buffer.str();
os << buffer.str();
else
{
auto &s = buffer.str();
@@ -203,14 +216,14 @@ Window &operator<<(Window &w, const BasicBuffer<CharT> &buffer)
for (size_t i = 0; i < s.size(); ++i)
{
for (; p != ps.end() && p->position() == i; ++p)
w << *p;
w << s[i];
os << *p;
os << s[i];
}
// load remaining properties
for (; p != ps.end(); ++p)
w << *p;
os << *p;
}
return w;
return os;
}
}

View File

@@ -26,8 +26,8 @@
#include <boost/locale/conversion.hpp>
#include <algorithm>
#include <fstream>
#include <sstream>
#include "actions.h"
#include "browser.h"
#include "charset.h"
#include "display.h"

View File

@@ -44,23 +44,24 @@ void drawHeader()
if (!Config.header_visibility)
return;
if (Config.new_design)
switch (Config.design)
{
std::wstring title = myScreen->title();
*wHeader << NC::XY(0, 3) << wclrtoeol;
*wHeader << NC::Format::Bold << Config.alternative_ui_separator_color;
mvwhline(wHeader->raw(), 2, 0, 0, COLS);
mvwhline(wHeader->raw(), 4, 0, 0, COLS);
*wHeader << NC::XY((COLS-wideLength(title))/2, 3);
*wHeader << Config.header_color << title << NC::Color::End;
*wHeader << NC::Color::End << NC::Format::NoBold;
}
else
{
*wHeader << NC::XY(0, 0) << wclrtoeol << NC::Format::Bold << myScreen->title() << NC::Format::NoBold;
*wHeader << Config.volume_color;
*wHeader << NC::XY(wHeader->getWidth()-VolumeState.length(), 0) << VolumeState;
*wHeader << NC::Color::End;
case Design::Classic:
*wHeader << NC::XY(0, 0) << wclrtoeol << NC::Format::Bold << myScreen->title() << NC::Format::NoBold;
*wHeader << Config.volume_color;
*wHeader << NC::XY(wHeader->getWidth()-VolumeState.length(), 0) << VolumeState;
*wHeader << NC::Color::End;
break;
case Design::Alternative:
std::wstring title = myScreen->title();
*wHeader << NC::XY(0, 3) << wclrtoeol;
*wHeader << NC::Format::Bold << Config.alternative_ui_separator_color;
mvwhline(wHeader->raw(), 2, 0, 0, COLS);
mvwhline(wHeader->raw(), 4, 0, 0, COLS);
*wHeader << NC::XY((COLS-wideLength(title))/2, 3);
*wHeader << Config.header_color << title << NC::Color::End;
*wHeader << NC::Color::End << NC::Format::NoBold;
break;
}
wHeader->refresh();
}

View File

@@ -68,13 +68,13 @@ bool LocaleBasedItemSorting::operator()(const MPD::Item &a, const MPD::Item &b)
case MPD::itSong:
switch (m_sort_mode)
{
case smName:
case SortMode::Name:
result = m_cmp(*a.song, *b.song);
break;
case smMTime:
case SortMode::ModificationTime:
result = a.song->getMTime() > b.song->getMTime();
break;
case smCustomFormat:
case SortMode::CustomFormat:
result = m_cmp(a.song->toString(Config.browser_sort_format, Config.tags_separator),
b.song->toString(Config.browser_sort_format, Config.tags_separator));
break;

View File

@@ -18,10 +18,14 @@
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#ifndef NCMPCPP_UTILITY_CONVERSION_H
#define NCMPCPP_UTILITY_CONVERSION_H
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/type_traits/is_unsigned.hpp>
#include "config.h"
#include "gcc.h"
struct ConversionError
@@ -35,7 +39,7 @@ private:
std::string m_source_value;
};
struct OutOfBounds
struct OutOfBounds : std::exception
{
const std::string &errorMessage() { return m_error_message; }
@@ -43,23 +47,25 @@ struct OutOfBounds
GNUC_NORETURN static void raise(const Type &value, const Type &lbound, const Type &ubound)
{
throw OutOfBounds((boost::format(
"Value is out of bounds ([%1%, %2%] expected, %3% given)") % lbound % ubound % value).str());
"value is out of bounds ([%1%, %2%] expected, %3% given)") % lbound % ubound % value).str());
}
template <typename Type>
GNUC_NORETURN static void raiseLower(const Type &value, const Type &lbound)
{
throw OutOfBounds((boost::format(
"Value is out of bounds ([%1%, ->) expected, %2% given)") % lbound % value).str());
"value is out of bounds ([%1%, ->) expected, %2% given)") % lbound % value).str());
}
template <typename Type>
GNUC_NORETURN static void raiseUpper(const Type &value, const Type &ubound)
{
throw OutOfBounds((boost::format(
"Value is out of bounds ((<-, %1%] expected, %2% given)") % ubound % value).str());
"value is out of bounds ((<-, %1%] expected, %2% given)") % ubound % value).str());
}
virtual const char *what() const noexcept OVERRIDE { return m_error_message.c_str(); }
private:
OutOfBounds(std::string msg) : m_error_message(msg) { }
@@ -115,3 +121,5 @@ void upperBoundCheck(const Type &value, const Type &ubound)
if (value > ubound)
OutOfBounds::raiseUpper(value, ubound);
}
#endif // NCMPCPP_UTILITY_CONVERSION_H

37
src/utility/functional.h Normal file
View File

@@ -0,0 +1,37 @@
/***************************************************************************
* 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_UTILITY_FUNCTIONAL_H
#define NCMPCPP_UTILITY_FUNCTIONAL_H
#include <utility>
// identity function object
struct id_
{
template <typename ValueT>
constexpr auto operator()(ValueT &&v) const noexcept
-> decltype(std::forward<ValueT>(v))
{
return std::forward<ValueT>(v);
}
};
#endif // NCMPCPP_UTILITY_FUNCTIONAL_H

View File

@@ -0,0 +1,82 @@
/***************************************************************************
* 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 <boost/regex.hpp>
#include <iostream>
#include "utility/option_parser.h"
bool option_parser::run(std::istream &is)
{
// quoted value. leftmost and rightmost quotation marks are the delimiters.
boost::regex quoted("(\\w+)\\h*=\\h*\"(.*)\"[^\"]*");
// unquoted value. whitespaces get trimmed.
boost::regex unquoted("(\\w+)\\h*=\\h*(.*?)\\h*");
boost::smatch match;
std::string line;
while (std::getline(is, line))
{
if (boost::regex_match(line, match, quoted)
|| boost::regex_match(line, match, unquoted))
{
std::string option = match[1];
auto it = m_parsers.find(option);
if (it != m_parsers.end())
{
try {
it->second.parse(match[2]);
} catch (std::exception &e) {
std::cerr << "Error while processing option \"" << option << "\": " << e.what() << "\n";
return false;
}
}
else
{
std::cerr << "Unknown option: " << option << "\n";
return false;
}
}
}
for (auto &p : m_parsers)
{
if (!p.second.defined())
{
try {
p.second.run_default();
} catch (std::exception &e) {
std::cerr << "Error while finalizing option \"" << p.first << "\": " << e.what() << "\n";
return false;
}
}
}
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));
}

131
src/utility/option_parser.h Normal file
View File

@@ -0,0 +1,131 @@
/***************************************************************************
* 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_UTILITY_OPTION_PARSER_H
#define NCMPCPP_UTILITY_OPTION_PARSER_H
#include <boost/lexical_cast.hpp>
#include <cassert>
#include <stdexcept>
#include <unordered_map>
#include "helpers.h"
#include "strbuffer.h"
#include "utility/functional.h"
struct option_parser
{
typedef std::function<void(std::string &&)> parser_t;
typedef std::function<void()> default_t;
struct worker
{
worker() { }
template <typename ParserT, typename DefaultT>
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)
throw std::runtime_error("option already defined");
m_parser(std::forward<ValueT>(value));
m_defined = true;
}
bool defined() const { return m_defined; }
void run_default() const { m_default(); }
private:
bool m_defined;
parser_t m_parser;
default_t m_default;
};
template <typename ParserT, typename DefaultT>
void add(const std::string &option, ParserT &&p, DefaultT &&d)
{
assert(m_parsers.count(option) == 0);
m_parsers[option] = worker(std::forward<ParserT>(p), std::forward<DefaultT>(d));
}
template <typename WorkerT>
void add(const std::string &option, WorkerT &&w)
{
assert(m_parsers.count(option) == 0);
m_parsers[option] = std::forward<WorkerT>(w);
}
bool run(std::istream &is);
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 [&arg, map](std::string &&v) {
try {
arg = map(boost::lexical_cast<IntermediateT>(v));
} catch (boost::bad_lexical_cast &) {
throw std::runtime_error("invalid value: " + v);
}
};
}
template <typename ArgT, typename ValueT>
option_parser::default_t defaults_to(ArgT &arg, ValueT &&value)
{
return [&arg, value] {
arg = std::move(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, 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
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) {
return map(stringToBuffer(s));
}), defaults_to(arg, map(std::forward<ValueT>(value))));
}
option_parser::worker yes_no(bool &arg, bool value);
#endif // NCMPCPP_UTILITY_OPTION_PARSER_H

View File

@@ -21,6 +21,7 @@
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <readline/history.h>
#include <readline/readline.h>
@@ -158,6 +159,196 @@ int add_base()
namespace NC {//
std::ostream &operator<<(std::ostream &os, Color c)
{
switch (c)
{
case Color::Default:
os << "default";
break;
case Color::Black:
os << "black";
break;
case Color::Red:
os << "red";
break;
case Color::Green:
os << "green";
break;
case Color::Yellow:
os << "yellow";
break;
case Color::Blue:
os << "blue";
break;
case Color::Magenta:
os << "magenta";
break;
case Color::Cyan:
os << "cyan";
break;
case Color::White:
os << "white";
break;
case Color::End:
os << "color_end";
break;
}
return os;
}
std::istream &operator>>(std::istream &is, Color &c)
{
std::string sc;
is >> sc;
if (sc == "default")
c = Color::Default;
else if (sc == "black")
c = Color::Black;
else if (sc == "red")
c = Color::Red;
else if (sc == "green")
c = Color::Green;
else if (sc == "yellow")
c = Color::Yellow;
else if (sc == "blue")
c = Color::Blue;
else if (sc == "magenta")
c = Color::Magenta;
else if (sc == "cyan")
c = Color::Cyan;
else if (sc == "white")
c = Color::White;
else if (sc == "color_end")
c = Color::End;
else
is.setstate(std::ios::failbit);
return is;
}
std::ostream &operator<<(std::ostream &os, Format f)
{
switch (f)
{
case Format::None:
os << "none";
break;
case Format::Bold:
os << "bold";
break;
case Format::NoBold:
os << "bold";
break;
case Format::Underline:
os << "underline";
break;
case Format::NoUnderline:
os << "no_underline";
break;
case Format::Reverse:
os << "reverse";
break;
case Format::NoReverse:
os << "no_reverse";
break;
case Format::AltCharset:
os << "alt_charset";
break;
case Format::NoAltCharset:
os << "no_alt_charset";
break;
}
return os;
}
std::ostream &operator<<(std::ostream &os, Border b)
{
switch (b)
{
case Border::None:
os << "none";
break;
case Border::Black:
os << "black";
break;
case Border::Red:
os << "red";
break;
case Border::Green:
os << "green";
break;
case Border::Yellow:
os << "yellow";
break;
case Border::Blue:
os << "blue";
break;
case Border::Magenta:
os << "magenta";
break;
case Border::Cyan:
os << "cyan";
break;
case Border::White:
os << "white";
break;
}
return os;
}
std::istream &operator>>(std::istream &is, Border &b)
{
std::string sb;
is >> sb;
if (sb == "none")
b = Border::None;
else if (sb == "black")
b = Border::Black;
else if (sb == "red")
b = Border::Red;
else if (sb == "green")
b = Border::Green;
else if (sb == "yellow")
b = Border::Yellow;
else if (sb == "blue")
b = Border::Blue;
else if (sb == "magenta")
b = Border::Magenta;
else if (sb == "cyan")
b = Border::Cyan;
else if (sb == "white")
b = Border::White;
else
is.setstate(std::ios::failbit);
return is;
}
std::ostream &operator<<(std::ostream &os, Scroll s)
{
switch (s)
{
case Scroll::Up:
os << "scroll_up";
break;
case Scroll::Down:
os << "scroll_down";
break;
case Scroll::PageUp:
os << "scroll_page_up";
break;
case Scroll::PageDown:
os << "scroll_page_down";
break;
case Scroll::Home:
os << "scroll_home";
break;
case Scroll::End:
os << "scroll_end";
break;
}
return os;
}
void initScreen(GNUC_UNUSED const char *window_title, bool enable_colors)
{
const int ColorsTable[] =

View File

@@ -114,6 +114,9 @@ namespace NC {//
/// Colors used by NCurses
enum class Color { Default, Black, Red, Green, Yellow, Blue, Magenta, Cyan, White, End };
std::ostream &operator<<(std::ostream &os, Color c);
std::istream &operator>>(std::istream &is, Color &c);
/// Format flags used by NCurses
enum class Format {
None,
@@ -123,12 +126,19 @@ enum class Format {
AltCharset, NoAltCharset
};
std::ostream &operator<<(std::ostream &os, Format f);
/// Available border colors for window
enum class Border { None, Black, Red, Green, Yellow, Blue, Magenta, Cyan, White };
std::ostream &operator<<(std::ostream &os, Border b);
std::istream &operator>>(std::istream &is, Border &b);
/// This indicates how much the window has to be scrolled
enum class Scroll { Up, Down, PageUp, PageDown, Home, End };
std::ostream &operator<<(std::ostream &os, Scroll s);
/// Helper function that is invoked each time one will want
/// to obtain string from Window::getString() function
/// @see Window::getString()