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

@@ -12,7 +12,7 @@
## file which defines that while launching ncmpcpp.
##
#
#ncmpcpp_directory = "~/.ncmpcpp"
#ncmpcpp_directory = ~/.ncmpcpp
#
##
## Directory for storing downloaded lyrics. It
@@ -20,21 +20,21 @@
## (eg. ncmpc) also use that location.
##
#
#lyrics_directory = "~/.lyrics"
#lyrics_directory = ~/.lyrics
#
##### connection settings #####
#
## set it in order to make tag editor and renaming files work properly
#mpd_host = localhost
#
#mpd_host = "localhost"
#mpd_port = 6600
#
#mpd_port = "6600"
#mpd_connection_timeout = 5
#
#mpd_music_dir = ""
## Needed for tag editor and file operations to work.
##
#mpd_music_dir = ~/music
#
#mpd_connection_timeout = "5"
#
#mpd_crossfade_time = "5"
#mpd_crossfade_time = 5
#
##### music visualizer #####
##
@@ -46,19 +46,13 @@
##
## audio_output {
## type "fifo"
## name "My FIFO"
## name "Visualizer feed"
## path "/tmp/mpd.fifo"
## format "44100:16:2"
## }
##
#
##
## If you set format to 44100:16:2, make it 'yes'.
##
#
#visualizer_in_stereo = "no"
#
#visualizer_fifo_path = ""
#visualizer_fifo_path = /tmp/mpd.fifo
#
##
## Note: Below parameter is needed for ncmpcpp
@@ -68,7 +62,12 @@
## are some problems with it.
##
#
#visualizer_output_name = ""
#visualizer_output_name = Visualizer feed
#
##
## If you set format to 44100:16:2, make it 'yes'.
##
#visualizer_in_stereo = yes
#
##
## Note: Below parameter defines how often ncmpcpp
@@ -78,14 +77,16 @@
## Keep in mind that sane values start with >=10.
##
#
#visualizer_sync_interval = "30"
#visualizer_sync_interval = 30
#
##
## Note: To enable spectrum frequency visualization
## you need to compile ncmpcpp with fftw3 support.
##
#
#visualizer_type = "wave" (spectrum/wave)
## Available values: spectrum, wave.
##
#visualizer_type = wave
#
#visualizer_look = "◆│"
#
@@ -99,7 +100,7 @@
## supports charset detection by checking output
## of `ncmpcpp --version`.
##
## Note: Since MPD uses utf8 by default, setting
## Note: Since MPD uses UTF-8 by default, setting
## this option makes sense only if your encoding
## is different.
##
@@ -108,13 +109,14 @@
#
##### delays #####
#
## delay after playlist highlighting will be disabled (0 = don't disable)
## Time of inactivity (in seconds) after playlist
## highlighting will be disabled (0 = always on).
##
#playlist_disable_highlight_delay = 5
#
#playlist_disable_highlight_delay = "5"
#
## defines how long various messages are supposed to be visible
#
#message_delay_time = "4"
## Defines how long messages are supposed to be visible.
##
#message_delay_time = 5
#
##### song format #####
##
@@ -151,9 +153,9 @@
## you'll get nothing.
##
## text can also have different color than the main window has,
## eg. if you want length to be green, write $3%l$9
## eg. if you want length to be green, write "$3%l$9".
##
## available values:
## Available values:
##
## - 0 - default window color (discards all other colors)
## - 1 - black
@@ -169,24 +171,24 @@
## Note: colors can be nested.
##
#
#song_list_format = "{%a - }{%t}|{$8%f$9}$R{$3(%l)$9}"
#song_list_format = {%a - }{%t}|{$8%f$9}$R{$3(%l)$9}
#
#song_status_format = "{{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}"
#song_status_format = {{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}
#
#song_library_format = "{%n - }{%t}|{%f}"
#song_library_format = {%n - }{%t}|{%f}
#
#tag_editor_album_format = "{(%y) }%b"
#tag_editor_album_format = {(%y) }%b
#
##
## Note: Below variables are used for sorting songs in browser.
## The sort mode determines how songs are sorted, and can be used
## in combination with a sort format to specify a custom sorting format.
## Possible values for browser_sort_mode are "name", "mtime" and "format".
## Available values for browser_sort_mode are "name", "mtime" and "format".
##
#
#browser_sort_mode = "name"
#browser_sort_mode = name
#
#browser_sort_format = "{%a - }{%t}|{%f} {(%l)}"
#browser_sort_format = {%a - }{%t}|{%f} {(%l)}
#
##
## Note: Below variables are for alternative version of user's interface.
@@ -205,30 +207,29 @@
## with reversed colors.
##
#
#alternative_header_first_line_format = "$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b"
#alternative_header_first_line_format = $b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b
#
#alternative_header_second_line_format = "{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}"
#alternative_header_second_line_format = {{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}
#
##
## Note: Below variables also supports
## text attributes listed above.
## Note: below variables also support text attributes listed above.
##
#
#now_playing_prefix = "$b"
#now_playing_prefix = $b
#
#now_playing_suffix = "$/b"
#now_playing_suffix = $/b
#
#browser_playlist_prefix = "$2playlist$9 "
#
#selected_item_prefix = "$6"
#selected_item_prefix = $6
#
#selected_item_suffix = "$9"
#selected_item_suffix = $9
#
#modified_item_prefix = "$3> $9"
#modified_item_prefix = $3> $9
#
## colors are not supported for below variable
#
#song_window_title_format = "{%a - }{%t}|{%f}"
## Note: colors are not supported for below variable.
##
#song_window_title_format = {%a - }{%t}|{%f}
#
##### columns settings #####
##
@@ -266,7 +267,7 @@
## not available.
##
#
#song_columns_list_format = "(7f)[green]{l} (25)[cyan]{a} (40)[]{t|f} (30)[red]{b}"
#song_columns_list_format = (7f)[green]{l} (25)[cyan]{a} (40)[]{t|f} (30)[red]{b}
#
##### various settings #####
#
@@ -279,75 +280,86 @@
##
#execute_on_song_change = ""
#
#playlist_show_remaining_time = "no"
#playlist_show_remaining_time = no
#
#playlist_shorten_total_times = "no"
#playlist_shorten_total_times = no
#
#playlist_separate_albums = "no"
#playlist_separate_albums = no
#
#playlist_display_mode = "classic" (classic/columns)
##
## Note: Possible display modes: classic, columns.
##
#playlist_display_mode = classic
#
#browser_display_mode = "classic" (classic/columns)
#browser_display_mode = classic
#
#search_engine_display_mode = "classic" (classic/columns)
#search_engine_display_mode = classic
#
#playlist_editor_display_mode = "classic" (classic/columns)
#playlist_editor_display_mode = classic
#
#discard_colors_if_item_is_selected = "yes"
#discard_colors_if_item_is_selected = yes
#
#incremental_seeking = "yes"
#incremental_seeking = yes
#
#seek_time = "1"
#seek_time = 1
#
#volume_change_step = "1"
#volume_change_step = 1
#
#autocenter_mode = "no"
#autocenter_mode = no
#
#centered_cursor = "no"
#centered_cursor = no
#
##
## Note: You can specify third character which will
## be used to build 'empty' part of progressbar.
##
#progressbar_look = "=>"
#progressbar_look = =>
#
#progressbar_boldness = "yes"
#progressbar_boldness = yes
#
#default_place_to_search_in = "database" (database/playlist)
## Available values: database, playlist.
##
#default_place_to_search_in = database
#
#user_interface = "classic" (classic/alternative)
## Available values: classic, alternative.
##
#user_interface = classic
#
#media_library_left_column = "a" (possible values: a,y,g,c,p, legend above)
## Available values: artist, date, genre, composer, performer.
##
#media_library_primary_tag = artist
#
#default_find_mode = "wrapped" (wrapped/normal)
## Available values: wrapped, normal.
##
#default_find_mode = wrapped
#
#default_space_mode = "add" (add/select)
## Available values: add, select.
##
#default_space_mode = add
#
#default_tag_editor_pattern = "%n - %t"
#default_tag_editor_pattern = %n - %t
#
#header_visibility = "yes"
#header_visibility = yes
#
#statusbar_visibility = "yes"
#statusbar_visibility = yes
#
#titles_visibility = "yes"
#titles_visibility = yes
#
#header_text_scrolling = "yes"
#header_text_scrolling = yes
#
#fancy_scrolling = "yes"
#cyclic_scrolling = no
#
#cyclic_scrolling = "no"
#lines_scrolled = 2
#
#lines_scrolled = "2"
#follow_now_playing_lyrics = no
#
#follow_now_playing_lyrics = "no"
#fetch_lyrics_for_current_song_in_background = no
#
#fetch_lyrics_for_current_song_in_background = "no"
#store_lyrics_in_song_dir = no
#
#store_lyrics_in_song_dir = "no"
#generate_win32_compatible_filenames = yes
#
#generate_win32_compatible_filenames = "yes"
#
#allow_for_physical_item_deletion = "no"
#allow_for_physical_item_deletion = no
#
##
## Note: If you set this variable, ncmpcpp will try to
@@ -357,13 +369,13 @@
##
## Note: Language has to be expressed as an ISO 639 alpha-2 code.
##
#lastfm_preferred_language = ""
#lastfm_preferred_language = en
#
#ncmpc_like_songs_adding = "no" (enabled - add/remove, disabled - always add)
## Available values: add_remove, always_add.
##
#space_add_mode = always_add
#
#show_hidden_files_in_local_browser = "no"
#
#display_screens_numbers_on_start = "yes"
#show_hidden_files_in_local_browser = no
#
##
## How shall screen switcher work?
@@ -374,59 +386,61 @@
## Screens available for use: help, playlist, browser, search_engine,
## media_library, playlist_editor, tag_editor, outputs, visualizer, clock.
##
#screen_switcher_mode = "playlist, browser"
#screen_switcher_mode = playlist, browser
#
##
## Note: You can define startup screen for ncmpcpp
## by choosing screen from the list above.
##
#startup_screen = "playlist"
#startup_screen = playlist
#
##
## Default width of locked screen (in %).
## Acceptable values are from 20 to 80.
##
#
#locked_screen_width_part = "50"
#locked_screen_width_part = 50
#
#ask_for_locked_screen_width_part = "yes"
#ask_for_locked_screen_width_part = yes
#
#jump_to_now_playing_song_at_start = "yes"
#jump_to_now_playing_song_at_start = yes
#
#ask_before_clearing_main_playlist = "no"
#ask_before_clearing_playlists = yes
#
#clock_display_seconds = "no"
#clock_display_seconds = no
#
#display_volume_level = "yes"
#display_volume_level = yes
#
#display_bitrate = "no"
#display_bitrate = no
#
#display_remaining_time = "no"
#display_remaining_time = no
#
#regular_expressions = "none" (none/basic/extended)
## Available values: none, basic, extended.
##
#regular_expressions = none
#
##
## Note: If below is enabled, ncmpcpp will ignore leading
## "The" word while sorting items in browser, tags in
## media library, etc.
##
#ignore_leading_the = "no"
#ignore_leading_the = no
#
#block_search_constraints_change_if_items_found = "yes"
#block_search_constraints_change_if_items_found = yes
#
#mouse_support = "yes"
#mouse_support = yes
#
#mouse_list_scroll_whole_page = "yes"
#mouse_list_scroll_whole_page = yes
#
#empty_tag_marker = "<empty>"
#empty_tag_marker = <empty>
#
#tags_separator = " | "
#
#tag_editor_extended_numeration = "no"
#tag_editor_extended_numeration = no
#
#media_library_sort_by_mtime = "no"
#media_library_sort_by_mtime = no
#
#enable_window_title = "yes"
#enable_window_title = yes
#
##
## Note: You can choose default search mode for search
@@ -440,47 +454,49 @@
## in database and local one for searching in current playlist)
##
#
#search_engine_default_search_mode = "1"
#search_engine_default_search_mode = 1
#
#external_editor = ""
#external_editor = nano
#
#use_console_editor = "no" (set to yes, if your editor is console app)
## Note: set to yes if external editor is a console application.
##
#use_console_editor = yes
#
##### colors definitions #####
#
#colors_enabled = "yes"
#colors_enabled = yes
#
#empty_tag_color = "cyan"
#empty_tag_color = cyan
#
#header_window_color = "default"
#header_window_color = default
#
#volume_color = "default"
#volume_color = default
#
#state_line_color = "default"
#state_line_color = default
#
#state_flags_color = "default"
#state_flags_color = default
#
#main_window_color = "yellow"
#main_window_color = yellow
#
#color1 = "white"
#color1 = white
#
#color2 = "green"
#color2 = green
#
#main_window_highlight_color = "yellow"
#main_window_highlight_color = yellow
#
#progressbar_color = "default"
#progressbar_color = default
#
#progressbar_elapsed_color = "default"
#progressbar_elapsed_color = default
#
#statusbar_color = "default"
#statusbar_color = default
#
#alternative_ui_separator_color = "black"
#alternative_ui_separator_color = black
#
#active_column_color = "red"
#active_column_color = red
#
#visualizer_color = "yellow"
#visualizer_color = yellow
#
#window_border_color = "green"
#window_border_color = green
#
#active_window_border = "red"
#active_window_border = red
#

View File

@@ -156,9 +156,6 @@ No need to describe it, huh?
.B header_text_scrolling = yes/no
If enabled, text in header window will scroll if its length is longer then actual screen width, otherwise it won't.
.TP
.B fancy_scrolling = yes/no
If enabled, content of other columns will be updated immediately while scrolling, otherwise only after you stop scrolling.
.TP
.B cyclic_scrolling = yes/no
If enabled, cyclic scrolling is used (e.g. if you press down arrow being at the end of list, it'll take you to the beginning)
.TP
@@ -243,8 +240,8 @@ 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
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
.B ncmpc_like_songs_adding = yes/no
If enabled, pressing space on item, which is already in playlist will remove it, otherwise add it again.
.B space_add_mode = add_remove/always_add
If set to add_remove, pressing space on item which is already in playlist will remove it, otherwise add it again.
.TP
.B show_hidden_files_in_local_browser = yes/no
Trigger for displaying in local browser files and directories that begin with '.'
@@ -309,8 +306,8 @@ Type of currently used regular expressions.
.B user_interface = classic/alternative
Default user interface used by ncmpcpp at start.
.TP
.B media_library_left_column = a/y/g/c/p
Default tag type for left column in media library. Legend for possible letters is in SONG FORMAT section.
.B media_library_primary_tag = artist/date/genre/composer/performer
Default tag type for leftmost column in media library.
.TP
.B empty_tag_marker = TEXT
Text that will be displayed, if requested tag is not set.

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()