From 4b933b29e14d57d4b3a47bd043829618e8abbabe Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Thu, 28 Aug 2014 18:57:16 +0200 Subject: [PATCH] settings: configuration file processing rewrite --- doc/config | 272 ++--- doc/ncmpcpp.1 | 11 +- src/Makefile.am | 9 +- src/actions.cpp | 249 +++-- src/bindings.h | 4 - src/browser.cpp | 49 +- src/{cmdargs.cpp => configuration.cpp} | 55 +- src/{cmdargs.h => configuration.h} | 10 +- src/display.cpp | 26 +- src/enums.cpp | 134 +++ src/enums.h | 42 + src/helpers.cpp | 2 +- src/helpers.h | 8 + src/lyrics.cpp | 4 +- src/ncmpcpp.cpp | 19 +- src/playlist.cpp | 46 +- src/playlist_editor.cpp | 26 +- src/screen_type.cpp | 2 + src/search_engine.cpp | 27 +- src/settings.cpp | 1404 +++++++++--------------- src/settings.h | 66 +- src/song.cpp | 15 +- src/song.h | 2 +- src/status.cpp | 272 ++--- src/statusbar.cpp | 26 +- src/strbuffer.h | 39 +- src/tag_editor.cpp | 2 +- src/title.cpp | 33 +- src/utility/comparators.cpp | 6 +- src/utility/conversion.h | 16 +- src/utility/functional.h | 37 + src/utility/option_parser.cpp | 82 ++ src/utility/option_parser.h | 131 +++ src/window.cpp | 191 ++++ src/window.h | 10 + 35 files changed, 1881 insertions(+), 1446 deletions(-) rename src/{cmdargs.cpp => configuration.cpp} (80%) rename src/{cmdargs.h => configuration.h} (89%) create mode 100644 src/enums.cpp create mode 100644 src/enums.h create mode 100644 src/utility/functional.h create mode 100644 src/utility/option_parser.cpp create mode 100644 src/utility/option_parser.h diff --git a/doc/config b/doc/config index cd0dfbba..19e33510 100644 --- a/doc/config +++ b/doc/config @@ -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_tag_marker = # #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 # diff --git a/doc/ncmpcpp.1 b/doc/ncmpcpp.1 index 741bdd66..65f5b5b7 100644 --- a/doc/ncmpcpp.1 +++ b/doc/ncmpcpp.1 @@ -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. diff --git a/src/Makefile.am b/src/Makefile.am index c807aa64..ee6e8cdd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/actions.cpp b/src/actions.cpp index 138aaefb..98db7169 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -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(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(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()); diff --git a/src/bindings.h b/src/bindings.h index 477655d4..0ec75332 100644 --- a/src/bindings.h +++ b/src/bindings.h @@ -99,10 +99,6 @@ struct Binding return m_actions[0]; } - const ActionChain &actions() const { - return m_actions; - } - private: ActionChain m_actions; }; diff --git a/src/browser.cpp b/src/browser.cpp index 7dd0dc73..0841761f 100644 --- a/src/browser.cpp +++ b/src/browser.cpp @@ -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(0, MainStartY, COLS, MainHeight, Config.columns_in_browser && Config.titles_visibility ? Display::Columns(COLS) : "", Config.main_color, NC::Border::None); + w = NC::Menu(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); diff --git a/src/cmdargs.cpp b/src/configuration.cpp similarity index 80% rename from src/cmdargs.cpp rename to src/configuration.cpp index 871a4ed1..c25c2641 100644 --- a/src/cmdargs.cpp +++ b/src/configuration.cpp @@ -18,11 +18,12 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include #include #include #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()->default_value("localhost"), "connect to server at host") ("port,p", po::value()->default_value(6600), "connect to server at port") ("config,c", po::value(&config_path)->default_value("~/.ncmpcpp/config"), "specify configuration file") ("bindigs,b", po::value(&bindings_path)->default_value("~/.ncmpcpp/bindings"), "specify bindings file") - ("screen,s", po::value(), "specify the startup screen") + ("screen,s", po::value(), "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()); - if (vm.count("port")) + if (!vm["port"].defaulted()) Mpd.SetPort(vm["port"].as()); + 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; } diff --git a/src/cmdargs.h b/src/configuration.h similarity index 89% rename from src/cmdargs.h rename to src/configuration.h index 1a087737..1454dc5a 100644 --- a/src/cmdargs.h +++ b/src/configuration.h @@ -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 diff --git a/src/display.cpp b/src/display.cpp index 4b2ec515..ec9010ed 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -392,10 +392,15 @@ void Display::Items(NC::Menu &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 &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(); diff --git a/src/enums.cpp b/src/enums.cpp new file mode 100644 index 00000000..b18fb36e --- /dev/null +++ b/src/enums.cpp @@ -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; +} diff --git a/src/enums.h b/src/enums.h new file mode 100644 index 00000000..4fcdcb4e --- /dev/null +++ b/src/enums.h @@ -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 + +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 diff --git a/src/helpers.cpp b/src/helpers.cpp index 7906a305..0e93a99a 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -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) diff --git a/src/helpers.h b/src/helpers.h index 7f8b54d1..ff8abd22 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -434,6 +434,14 @@ void stringToBuffer(const std::basic_string &s, NC::BasicBuffer &b stringToBuffer(s.begin(), s.end(), buf); } +template +NC::BasicBuffer stringToBuffer(const std::basic_string &s) +{ + NC::BasicBuffer result; + stringToBuffer(s, result); + return result; +} + template void ShowTime(T &buf, size_t length, bool short_names) { const unsigned MINUTE = 60; diff --git a/src/lyrics.cpp b/src/lyrics.cpp index 2c3c240a..3a4f3efc 100644 --- a/src/lyrics.cpp +++ b/src/lyrics.cpp @@ -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(); diff --git a/src/ncmpcpp.cpp b/src/ncmpcpp.cpp index 9dfb0e77..e646ec70 100644 --- a/src/ncmpcpp.cpp +++ b/src/ncmpcpp.cpp @@ -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) diff --git a/src/playlist.cpp b/src/playlist.cpp index 26f949d6..dd65d692 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -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(0, MainStartY, COLS, MainHeight, Config.columns_in_playlist && Config.titles_visibility ? Display::Columns(COLS) : "", Config.main_color, NC::Border::None); + w = NC::Menu(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; } diff --git a/src/playlist_editor.cpp b/src/playlist_editor.cpp index dddb806d..7a5c5255 100644 --- a/src/playlist_editor.cpp +++ b/src/playlist_editor.cpp @@ -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; } diff --git a/src/screen_type.cpp b/src/screen_type.cpp index eb639889..20f3c4f2 100644 --- a/src/screen_type.cpp +++ b/src/screen_type.cpp @@ -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: diff --git a/src/search_engine.cpp b/src/search_engine.cpp index 41827569..7325ed71 100644 --- a/src/search_engine.cpp +++ b/src/search_engine.cpp @@ -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(); diff --git a/src/settings.cpp b/src/settings.cpp index 941e73f5..afc8d1e1 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -18,38 +18,14 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#ifdef WIN32 -# include -# define _WIN32_IE 0x0400 -# include -#else -# include -#endif // WIN32 -#include -#include -#include -#include -#include +#include #include -#include #include -#include -#include "actions.h" -#include "browser.h" -#include "clock.h" -#include "global.h" -#include "help.h" -#include "helpers.h" -#include "lyrics.h" -#include "media_library.h" -#include "outputs.h" -#include "playlist.h" -#include "playlist_editor.h" -#include "search_engine.h" +#include "configuration.h" #include "settings.h" -#include "tag_editor.h" -#include "visualizer.h" +#include "utility/conversion.h" +#include "utility/option_parser.h" #include "utility/type_conversions.h" #ifdef HAVE_LANGINFO_H @@ -58,846 +34,32 @@ Configuration Config; -namespace +namespace { + +std::string remove_dollar_formatting(const std::string &s) { - std::string GetOptionName(const std::string &s) + std::string result; + for (size_t i = 0; i < s.size(); ++i) { - size_t equal = s.find('='); - if (equal == std::string::npos) - return ""; - std::string result = s.substr(0, equal); - boost::trim(result); - return result; - } - - std::string RemoveDollarFormatting(const std::string &s) - { - std::string result; - for (size_t i = 0; i < s.size(); ++i) - { - if (s[i] != '$') - result += s[i]; - else - ++i; - } - return result; + if (s[i] != '$') + result += s[i]; + else + ++i; } + return result; } -void CreateDir(const std::string &dir) +std::pair, std::string> generate_columns(const std::string &format) { - mkdir(dir.c_str() -# ifndef WIN32 - , 0755 -# endif // !WIN32 - ); -} - -void Configuration::SetDefaults() -{ - mpd_host = "localhost"; - empty_tag = ""; - tags_separator = " | "; - song_list_columns_format = "(7f)[green]{l} (25)[cyan]{a} (40)[]{t|f} (30)[red]{b}"; - song_list_format = "{{%a - }{%t}|{$8%f$9}$R{$3(%l)$9}}"; - song_list_format_dollar_free = RemoveDollarFormatting(song_list_format); - song_status_format = "{{{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}}"; - song_status_format_no_colors = song_status_format; - song_window_title_format = "{{%a - }{%t}|{%f}}"; - song_library_format = "{{%n - }{%t}|{%f}}"; - browser_sort_format = "{{%a - }{%t}|{%f} {(%l)}}"; - tag_editor_album_format = "{{(%y) }%b}"; - new_header_first_line = "{$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b}"; - new_header_second_line = "{{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}}"; - browser_playlist_prefix << NC::Color::Red << "playlist" << NC::Color::End << ' '; - progressbar = L"=>\0"; - visualizer_chars = L"◆│"; - pattern = "%n - %t"; - selected_item_prefix << NC::Color::Magenta; - selected_item_suffix << NC::Color::End; - now_playing_prefix << NC::Format::Bold; - now_playing_suffix << NC::Format::NoBold; - modified_item_prefix << NC::Color::Green << "> " << NC::Color::End; - color1 = NC::Color::White; - color2 = NC::Color::Green; - empty_tags_color = NC::Color::Cyan; - header_color = NC::Color::Default; - volume_color = NC::Color::Default; - state_line_color = NC::Color::Default; - state_flags_color = NC::Color::Default; - main_color = NC::Color::Yellow; - main_highlight_color = main_color; - progressbar_color = NC::Color::Default; - progressbar_elapsed_color = NC::Color::Default; - statusbar_color = NC::Color::Default; - alternative_ui_separator_color = NC::Color::Black; - active_column_color = NC::Color::Red; - window_border = NC::Border::Green; - active_window_border = NC::Border::Red; - visualizer_color = NC::Color::Yellow; - media_lib_primary_tag = MPD_TAG_ARTIST; - colors_enabled = true; - playlist_show_remaining_time = false; - playlist_shorten_total_times = false; - playlist_separate_albums = false; - columns_in_playlist = false; - columns_in_browser = false; - columns_in_search_engine = false; - columns_in_playlist_editor = false; - header_visibility = true; - header_text_scrolling = true; - statusbar_visibility = true; - titles_visibility = true; - centered_cursor = false; - screen_switcher_previous = false; - autocenter_mode = false; - wrapped_search = true; - space_selects = false; - ncmpc_like_songs_adding = false; - incremental_seeking = true; - now_playing_lyrics = false; - fetch_lyrics_in_background = false; - local_browser_show_hidden_files = false; - search_in_db = true; - jump_to_now_playing_song_at_start = true; - clock_display_seconds = false; - display_volume_level = true; - display_bitrate = false; - display_remaining_time = false; - ignore_leading_the = false; - block_search_constraints_change = true; - use_console_editor = false; - use_cyclic_scrolling = false; - ask_before_clearing_main_playlist = false; - mouse_support = true; - mouse_list_scroll_whole_page = true; - new_design = false; - visualizer_use_wave = true; - visualizer_in_stereo = false; - media_library_sort_by_mtime = false; - tag_editor_extended_numeration = false; - discard_colors_if_item_is_selected = true; - store_lyrics_in_song_dir = false; - generate_win32_compatible_filenames = true; - ask_for_locked_screen_width_part = true; - allow_for_physical_item_deletion = false; - progressbar_boldness = true; - set_window_title = true; - mpd_port = 6600; - mpd_connection_timeout = 15; - crossfade_time = 5; - seek_time = 1; - volume_change_step = 1; - playlist_disable_highlight_delay = boost::posix_time::seconds(5); - message_delay_time = 4; - lyrics_db = 0; - regex_type = boost::regex::literal | boost::regex::icase; - lines_scrolled = 2; - search_engine_default_search_mode = 0; - visualizer_sync_interval = boost::posix_time::seconds(30); - locked_screen_width_part = 0.5; - selected_item_prefix_length = 0; - selected_item_suffix_length = 0; - now_playing_suffix_length = 0; -# ifdef HAVE_LANGINFO_H - system_encoding = nl_langinfo(CODESET); - if (system_encoding == "UTF-8") // mpd uses utf-8 by default so no need to convert - system_encoding.clear(); -# endif // HAVE_LANGINFO_H - startup_screen_type = ScreenType::Playlist; - browser_sort_mode = smName; - // default screens sequence - screens_seq.push_back(ScreenType::Playlist); - screens_seq.push_back(ScreenType::Browser); -} - -Configuration::Configuration() -: playlist_disable_highlight_delay(0), visualizer_sync_interval(0) -{ -# ifdef WIN32 - ncmpcpp_directory = GetHomeDirectory() + "ncmpcpp/"; - lyrics_directory = ncmpcpp_directory + "lyrics/"; -# else - ncmpcpp_directory = GetHomeDirectory() + ".ncmpcpp/"; - lyrics_directory = GetHomeDirectory() + ".lyrics/"; -# endif // WIN32 -} - -const std::string &Configuration::GetHomeDirectory() -{ - if (!home_directory.empty()) - return home_directory; -# ifdef WIN32 - char path[MAX_PATH]; - SHGetSpecialFolderPath(0, path, CSIDL_PERSONAL, 0); - home_directory = path ? path : ""; - replace(home_directory.begin(), home_directory.end(), '\\', '/'); -# else - char *home = getenv("HOME"); - home_directory = home ? home : ""; -# endif // WIN32 - if (!home_directory.empty() && *home_directory.rbegin() != '/') - home_directory += '/'; - return home_directory; -} - -void Configuration::Read(const std::string &config_path) -{ - std::ifstream f(config_path.c_str()); - std::string cl, v, name; - - if (!f.is_open()) - return; - - while (!f.eof()) - { - getline(f, cl); - if (!cl.empty() && cl[0] != '#') - { - name = GetOptionName(cl); - v = getEnclosedString(cl, '"', '"', 0); - - if (name == "ncmpcpp_directory") - { - if (!v.empty()) - { - MakeProperPath(v); - ncmpcpp_directory = v; - } - } - else if (name == "lyrics_directory") - { - if (!v.empty()) - { - MakeProperPath(v); - lyrics_directory = v; - } - } - else if (name == "mpd_host") - { - if (!v.empty()) - mpd_host = v; - } - else if (name == "mpd_music_dir") - { - if (!v.empty()) - { - MakeProperPath(v); - mpd_music_dir = v; - } - } - else if (name == "visualizer_fifo_path") - { - if (!v.empty()) - visualizer_fifo_path = v; - } - else if (name == "visualizer_output_name") - { - if (!v.empty()) - visualizer_output_name = v; - } - else if (name == "mpd_port") - { - if (boost::lexical_cast(v)) - mpd_port = boost::lexical_cast(v); - } - else if (name == "mpd_connection_timeout") - { - if (boost::lexical_cast(v)) - mpd_connection_timeout = boost::lexical_cast(v); - } - else if (name == "mpd_crossfade_time") - { - if (boost::lexical_cast(v) > 0) - crossfade_time = boost::lexical_cast(v); - } - else if (name == "seek_time") - { - if (boost::lexical_cast(v) > 0) - seek_time = boost::lexical_cast(v); - } - else if (name == "volume_change_step") - { - if (boost::lexical_cast(v) > 0) - volume_change_step = boost::lexical_cast(v); - } - else if (name == "playlist_disable_highlight_delay") - { - playlist_disable_highlight_delay = boost::posix_time::seconds(boost::lexical_cast(v)); - } - else if (name == "message_delay_time") - { - if (boost::lexical_cast(v) > 0) - message_delay_time = boost::lexical_cast(v); - } - else if (name == "song_list_format") - { - if (!v.empty() && MPD::Song::isFormatOk("song_list_format", v)) - { - song_list_format = '{'; - song_list_format += v; - song_list_format += '}'; - song_list_format_dollar_free = RemoveDollarFormatting(song_list_format); - } - } - else if (name == "song_columns_list_format") - { - if (!v.empty()) - song_list_columns_format = v; - } - else if (name == "song_status_format") - { - if (!v.empty() && MPD::Song::isFormatOk("song_status_format", v)) - { - song_status_format = '{'; - song_status_format += v; - song_status_format += '}'; - // make version without colors - if (song_status_format.find("$") != std::string::npos) - { - NC::Buffer status_no_colors; - stringToBuffer(song_status_format, status_no_colors); - song_status_format_no_colors = status_no_colors.str(); - } - else - song_status_format_no_colors = song_status_format; - } - } - else if (name == "song_library_format") - { - if (!v.empty() && MPD::Song::isFormatOk("song_library_format", v)) - { - song_library_format = '{'; - song_library_format += v; - song_library_format += '}'; - } - } - else if (name == "tag_editor_album_format") - { - if (!v.empty() && MPD::Song::isFormatOk("tag_editor_album_format", v)) - { - tag_editor_album_format = '{'; - tag_editor_album_format += v; - tag_editor_album_format += '}'; - } - } - else if (name == "browser_sort_format") - { - if (!v.empty() && MPD::Song::isFormatOk("browser_sort_format", v)) - { - browser_sort_format = '{'; - browser_sort_format += v; - browser_sort_format += '}'; - } - } - else if (name == "external_editor") - { - if (!v.empty()) - external_editor = v; - } - else if (name == "system_encoding") - { - if (!v.empty()) - system_encoding = v; - } - else if (name == "execute_on_song_change") - { - if (!v.empty()) - execute_on_song_change = v; - } - else if (name == "alternative_header_first_line_format") - { - if (!v.empty() && MPD::Song::isFormatOk("alternative_header_first_line_format", v)) - { - new_header_first_line = '{'; - new_header_first_line += v; - new_header_first_line += '}'; - } - } - else if (name == "alternative_header_second_line_format") - { - if (!v.empty() && MPD::Song::isFormatOk("alternative_header_second_line_format", v)) - { - new_header_second_line = '{'; - new_header_second_line += v; - new_header_second_line += '}'; - } - } - else if (name == "lastfm_preferred_language") - { - if (!v.empty() && v != "en") - lastfm_preferred_language = v; - } - else if (name == "browser_playlist_prefix") - { - if (!v.empty()) - { - browser_playlist_prefix.clear(); - stringToBuffer(v, browser_playlist_prefix); - } - } - else if (name == "progressbar_look") - { - std::wstring pb = ToWString(v); - if (pb.length() < 2 || pb.length() > 3) - { - std::cerr << "Warning: length of progressbar_look should be either "; - std::cerr << "2 or 3, but it's " << pb.length() << ", discarding.\n"; - } - else - progressbar = pb; - // if two characters were specified, add third one as null - progressbar.resize(3); - } - else if (name == "visualizer_look") - { - std::wstring vc = ToWString(v); - if (vc.length() != 2) - { - std::cerr << "Warning: length of visualizer_look should be 2, but it's " << vc.length() << ", discarding.\n"; - } - else - visualizer_chars = vc; - } - else if (name == "default_tag_editor_pattern") - { - if (!v.empty()) - pattern = v; - } - else if (name == "selected_item_prefix") - { - if (!v.empty()) - { - selected_item_prefix.clear(); - stringToBuffer(v, selected_item_prefix); - selected_item_prefix_length = wideLength(ToWString(selected_item_prefix.str())); - } - } - else if (name == "selected_item_suffix") - { - if (!v.empty()) - { - selected_item_suffix.clear(); - stringToBuffer(v, selected_item_suffix); - selected_item_suffix_length = wideLength(ToWString(selected_item_suffix.str())); - } - } - else if (name == "now_playing_prefix") - { - if (!v.empty()) - { - now_playing_prefix.clear(); - stringToBuffer(v, now_playing_prefix); - now_playing_prefix_length = wideLength(ToWString(now_playing_prefix.str())); - } - } - else if (name == "now_playing_suffix") - { - if (!v.empty()) - { - now_playing_suffix.clear(); - stringToBuffer(v, now_playing_suffix); - now_playing_suffix_length = wideLength(ToWString(now_playing_suffix.str())); - } - } - else if (name == "modified_item_prefix") - { - if (!v.empty()) - { - modified_item_prefix.clear(); - stringToBuffer(v, modified_item_prefix); - } - } - else if (name == "color1") - { - if (!v.empty()) - color1 = stringToColor(v); - } - else if (name == "color2") - { - if (!v.empty()) - color2 = stringToColor(v); - } - else if (name == "colors_enabled") - { - colors_enabled = v == "yes"; - } - else if (name == "cyclic_scrolling") - { - use_cyclic_scrolling = v == "yes"; - } - else if (name == "playlist_show_remaining_time") - { - playlist_show_remaining_time = v == "yes"; - } - else if (name == "playlist_shorten_total_times") - { - playlist_shorten_total_times = v == "yes"; - } - else if (name == "playlist_separate_albums") - { - playlist_separate_albums = v == "yes"; - } - else if (name == "playlist_display_mode") - { - columns_in_playlist = v == "columns"; - } - else if (name == "browser_display_mode") - { - columns_in_browser = v == "columns"; - } - else if (name == "search_engine_display_mode") - { - columns_in_search_engine = v == "columns"; - } - else if (name == "playlist_editor_display_mode") - { - columns_in_playlist_editor = v == "columns"; - } - else if (name == "header_visibility") - { - header_visibility = v == "yes"; - } - else if (name == "header_text_scrolling") - { - header_text_scrolling = v == "yes"; - } - else if (name == "statusbar_visibility") - { - statusbar_visibility = v == "yes"; - } - else if (name == "titles_visibility") - { - titles_visibility = v == "yes"; - } - else if (name == "screen_switcher_mode") - { - if (v == "previous") - screen_switcher_previous = true; - else - { - screen_switcher_previous = false; - screens_seq.clear(); - boost::sregex_token_iterator i(v.begin(), v.end(), boost::regex("\\w+")), j; - for (; i != j; ++i) - { - auto screen = stringtoStartupScreenType(*i); - if (screen != ScreenType::Unknown) - screens_seq.push_back(screen); - else - std::cerr << "screen_switcher_mode: unknown screen: " << *i << "\n"; - } - // throw away duplicates - screens_seq.unique(); - } - } - else if (name == "startup_screen") - { - startup_screen_type = stringtoStartupScreenType(v); - if (startup_screen_type == ScreenType::Unknown) - startup_screen_type = ScreenType::Playlist; - } - else if (name == "autocenter_mode") - { - autocenter_mode = v == "yes"; - } - else if (name == "centered_cursor") - { - centered_cursor = v == "yes"; - } - else if (name == "default_find_mode") - { - wrapped_search = v == "wrapped"; - } - else if (name == "default_space_mode") - { - space_selects = v == "select"; - } - else if (name == "incremental_seeking") - { - incremental_seeking = v == "yes"; - } - else if (name == "show_hidden_files_in_local_browser") - { - local_browser_show_hidden_files = v == "yes"; - } - else if (name == "follow_now_playing_lyrics") - { - now_playing_lyrics = v == "yes"; - } - else if (name == "fetch_lyrics_for_current_song_in_background") - { - fetch_lyrics_in_background = v == "yes"; - } - else if (name == "ncmpc_like_songs_adding") - { - ncmpc_like_songs_adding = v == "yes"; - } - else if (name == "default_place_to_search_in") - { - search_in_db = v == "database"; - } - else if (name == "jump_to_now_playing_song_at_start") - { - jump_to_now_playing_song_at_start = v == "yes"; - } - else if (name == "clock_display_seconds") - { - clock_display_seconds = v == "yes"; - } - else if (name == "display_volume_level") - { - display_volume_level = v == "yes"; - } - else if (name == "display_bitrate") - { - display_bitrate = v == "yes"; - } - else if (name == "display_remaining_time") - { - display_remaining_time = v == "yes"; - } - else if (name == "ignore_leading_the") - { - ignore_leading_the = v == "yes"; - } - else if (name == "use_console_editor") - { - use_console_editor = v == "yes"; - } - else if (name == "block_search_constraints_change_if_items_found") - { - block_search_constraints_change = v == "yes"; - } - else if (name == "ask_before_clearing_main_playlist") - { - ask_before_clearing_main_playlist = v == "yes"; - } - else if (name == "visualizer_type") - { - visualizer_use_wave = v == "wave"; - } - else if (name == "visualizer_in_stereo") - { - visualizer_in_stereo = v == "yes"; - } - else if (name == "mouse_support") - { - mouse_support = v == "yes"; - } - else if (name == "mouse_list_scroll_whole_page") - { - mouse_list_scroll_whole_page = v == "yes"; - } - else if (name == "user_interface") - { - new_design = v == "alternative"; - } - else if (name == "tag_editor_extended_numeration") - { - tag_editor_extended_numeration = v == "yes"; - } - else if (name == "discard_colors_if_item_is_selected") - { - discard_colors_if_item_is_selected = v == "yes"; - } - else if (name == "store_lyrics_in_song_dir") - { - if (mpd_music_dir.empty()) - { - std::cerr << "Warning: store_lyrics_in_song_dir = \"yes\" is "; - std::cerr << "not allowed without mpd_music_dir set, discarding.\n"; - } - else - store_lyrics_in_song_dir = v == "yes"; - } - else if (name == "generate_win32_compatible_filenames") - { - generate_win32_compatible_filenames = v == "yes"; - } - else if (name == "enable_window_title") - { - set_window_title = v == "yes"; - } - else if (name == "regular_expressions") - { - if (v == "none") - regex_type = boost::regex::literal | boost::regex::icase; - else if (v == "basic") - regex_type = boost::regex::basic | boost::regex::icase; - else if (v == "extended") - regex_type = boost::regex::extended | boost::regex::icase; - } - else if (name == "lines_scrolled") - { - if (!v.empty()) - lines_scrolled = boost::lexical_cast(v); - } - else if (name == "search_engine_default_search_mode") - { - if (!v.empty()) - { - unsigned mode = boost::lexical_cast(v); - if (--mode < 3) - search_engine_default_search_mode = mode; - } - } - else if (name == "visualizer_sync_interval") - { - unsigned interval = boost::lexical_cast(v); - if (interval) - visualizer_sync_interval = boost::posix_time::seconds(interval); - } - else if (name == "browser_sort_mode") - { - if (v == "mtime") - browser_sort_mode = smMTime; - else if (v == "format") - browser_sort_mode = smCustomFormat; - else - browser_sort_mode = smName; // "name" or invalid - } - else if (name == "locked_screen_width_part") - { - int part = boost::lexical_cast(v); - if (part) - locked_screen_width_part = part/100.0; - } - else if (name == "ask_for_locked_screen_width_part") - { - if (!v.empty()) - ask_for_locked_screen_width_part = v == "yes"; - } - else if (name == "allow_for_physical_item_deletion") - { - if (!v.empty()) - allow_for_physical_item_deletion = v == "yes"; - } - else if (name == "progressbar_boldness") - { - if (!v.empty()) - progressbar_boldness = v == "yes"; - } - else if (name == "song_window_title_format") - { - if (!v.empty() && MPD::Song::isFormatOk("song_window_title_format", v)) - { - song_window_title_format = '{'; - song_window_title_format += v; - song_window_title_format += '}'; - } - } - else if (name == "empty_tag_marker") - { - empty_tag = v; // is this case empty string is allowed - } - else if (name == "tags_separator") - { - if (!v.empty()) - tags_separator = v; - } - else if (name == "empty_tag_color") - { - if (!v.empty()) - empty_tags_color = stringToColor(v); - } - else if (name == "header_window_color") - { - if (!v.empty()) - header_color = stringToColor(v); - } - else if (name == "volume_color") - { - if (!v.empty()) - volume_color = stringToColor(v); - } - else if (name == "state_line_color") - { - if (!v.empty()) - state_line_color = stringToColor(v); - } - else if (name == "state_flags_color") - { - if (!v.empty()) - state_flags_color = stringToColor(v); - } - else if (name == "main_window_color") - { - if (!v.empty()) - main_color = stringToColor(v); - } - else if (name == "main_window_highlight_color") - { - if (!v.empty()) - main_highlight_color = stringToColor(v); - } - else if (name == "progressbar_color") - { - if (!v.empty()) - progressbar_color = stringToColor(v); - } - else if (name == "progressbar_elapsed_color") - { - if (!v.empty()) - progressbar_elapsed_color = stringToColor(v); - } - else if (name == "statusbar_color") - { - if (!v.empty()) - statusbar_color = stringToColor(v); - } - else if (name == "alternative_ui_separator_color") - { - if (!v.empty()) - alternative_ui_separator_color = stringToColor(v); - } - else if (name == "active_column_color") - { - if (!v.empty()) - active_column_color = stringToColor(v); - } - else if (name == "visualizer_color") - { - if (!v.empty()) - visualizer_color = stringToColor(v); - } - else if (name == "window_border_color") - { - if (!v.empty()) - window_border = stringToBorder(v); - } - else if (name == "active_window_border") - { - if (!v.empty()) - active_window_border = stringToBorder(v); - } - else if (name == "media_library_left_column") - { - if (!v.empty()) - media_lib_primary_tag = charToTagType(v[0]); - } - else if (name == "media_library_sort_by_mtime") - { - media_library_sort_by_mtime = v == "yes"; - } - else - std::cout << "Unknown option: " << name << ", ignoring.\n"; - } - } - f.close(); -} - -void Configuration::GenerateColumns() -{ - columns.clear(); + std::vector result; std::string width; size_t pos = 0; - while (!(width = getEnclosedString(song_list_columns_format, '(', ')', &pos)).empty()) + while (!(width = getEnclosedString(format, '(', ')', &pos)).empty()) { Column col; - col.color = stringToColor(getEnclosedString(song_list_columns_format, '[', ']', &pos)); - std::string tag_type = getEnclosedString(song_list_columns_format, '{', '}', &pos); - + col.color = stringToColor(getEnclosedString(format, '[', ']', &pos)); + std::string tag_type = getEnclosedString(format, '{', '}', &pos); + if (*width.rbegin() == 'f') { col.fixed = true; @@ -905,7 +67,7 @@ void Configuration::GenerateColumns() } else col.fixed = false; - + // alternative name size_t tag_type_colon_pos = tag_type.find(':'); if (tag_type_colon_pos != std::string::npos) @@ -913,16 +75,16 @@ void Configuration::GenerateColumns() col.name = ToWString(tag_type.substr(tag_type_colon_pos+1)); tag_type.resize(tag_type_colon_pos); } - + if (!tag_type.empty()) { size_t i = -1; - + // extract tag types in format a|b|c etc. do col.type += tag_type[(++i)++]; // nice one. while (tag_type[i] == '|'); - + // apply attributes for (; i < tag_type.length(); ++i) { @@ -939,17 +101,17 @@ void Configuration::GenerateColumns() } else // empty column col.display_empty_tag = 0; - + col.width = boost::lexical_cast(width); - columns.push_back(col); + result.push_back(col); } - + // calculate which column is the last one to have relative width and stretch it accordingly - if (!columns.empty()) + if (!result.empty()) { int stretch_limit = 0; - auto it = columns.rbegin(); - for (; it != columns.rend(); ++it) + auto it = result.rbegin(); + for (; it != result.rend(); ++it) { if (it->fixed) stretch_limit += it->width; @@ -957,35 +119,507 @@ void Configuration::GenerateColumns() break; } // if it's false, all columns are fixed - if (it != columns.rend()) + if (it != result.rend()) it->stretch_limit = stretch_limit; } - + + std::string string_format; // generate format for converting tags in columns to string for Playlist::SongInColumnsToString() char tag[] = "{% }|"; - song_in_columns_to_string_format = "{"; - for (auto it = columns.begin(); it != columns.end(); ++it) + string_format = "{"; + for (auto it = result.begin(); it != result.end(); ++it) { for (std::string::const_iterator j = it->type.begin(); j != it->type.end(); ++j) { tag[2] = *j; - song_in_columns_to_string_format += tag; + string_format += tag; } - *song_in_columns_to_string_format.rbegin() = ' '; + *string_format.rbegin() = ' '; } - if (song_in_columns_to_string_format.length() == 1) // only '{' - song_in_columns_to_string_format += '}'; + if (string_format.length() == 1) // only '{' + string_format += '}'; else - *song_in_columns_to_string_format.rbegin() = '}'; + *string_format.rbegin() = '}'; + + return std::make_pair(std::move(result), std::move(string_format)); } -void Configuration::MakeProperPath(std::string &dir) +void add_slash_at_the_end(std::string &s) { - if (dir.length() < 2) - return; - if (dir[0] == '~' && dir[1] == '/') - dir.replace(0, 2, home_directory); - std::replace(dir.begin(), dir.end(), '\\', '/'); - if (*dir.rbegin() != '/') - dir += '/'; + if (s.empty() || *s.rbegin() != '/') + { + s.resize(s.size()+1); + *s.rbegin() = '/'; + } +} + +std::string adjust_directory(std::string &&s) +{ + add_slash_at_the_end(s); + expand_home(s); + return s; +} + +std::string adjust_and_validate_format(std::string &&format) +{ + MPD::Song::validateFormat(format); + format = "{" + format + "}"; + return format; +} + +} + +bool Configuration::read(const std::string &config_path) +{ + std::string mpd_host; + unsigned mpd_port; + std::string columns_format; + + // keep the same order of variables as in configuration file + option_parser p; + p.add("ncmpcpp_directory", assign_default( + ncmpcpp_directory, "~/.ncmpcpp/", adjust_directory + )); + p.add("lyrics_directory", assign_default( + lyrics_directory, "~/.lyrics/", adjust_directory + )); + p.add("mpd_host", assign_default( + mpd_host, "localhost", [](std::string &&host) { + Mpd.SetHostname(host); + return host; + })); + p.add("mpd_port", assign_default( + mpd_port, 6600, [](unsigned port) { + Mpd.SetPort(port); + return port; + })); + p.add("mpd_music_dir", assign_default( + mpd_music_dir, "~/music", adjust_directory + )); + p.add("mpd_connection_timeout", assign_default( + mpd_connection_timeout, 5 + )); + p.add("mpd_crossfade_time", assign_default( + crossfade_time, 5 + )); + p.add("visualizer_fifo_path", assign_default( + visualizer_fifo_path, "/tmp/mpd.fifo" + )); + p.add("visualizer_output_name", assign_default( + visualizer_output_name, "Visualizer feed" + )); + p.add("visualizer_in_stereo", yes_no( + visualizer_in_stereo, true + )); + p.add("visualizer_sync_interval", assign_default( + visualizer_sync_interval, 30, [](unsigned v) { + return boost::posix_time::seconds(v); + })); + p.add("visualizer_type", option_parser::worker([this](std::string &&v) { + if (v == "wave") + visualizer_use_wave = true; + else if (v == "frequency") + visualizer_use_wave = false; + else + throw std::runtime_error("invalid argument: " + v); + }, defaults_to(visualizer_use_wave, true) + )); + p.add("visualizer_look", assign_default( + visualizer_chars, "◆│", [](std::string &&s) { + auto result = ToWString(std::move(s)); + typedef std::wstring::size_type size_type; + boundsCheck(result.size(), size_type(2), size_type(2)); + return result; + })); + p.add("system_encoding", assign_default( + system_encoding, "", [](std::string &&enc) { +# ifdef HAVE_LANGINFO_H + // try to autodetect system encoding + if (enc.empty()) + { + enc = nl_langinfo(CODESET); + if (enc == "UTF-8") // mpd uses utf-8 by default so no need to convert + enc.clear(); + } +# endif // HAVE_LANGINFO_H + return enc; + })); + p.add("playlist_disable_highlight_delay", assign_default( + playlist_disable_highlight_delay, 5, [](unsigned v) { + return boost::posix_time::seconds(v); + })); + p.add("message_delay_time", assign_default( + message_delay_time, 5 + )); + p.add("song_list_format", assign_default( + song_list_format, "{%a - }{%t}|{$8%f$9}$R{$3(%l)$9}", [this](std::string &&s) { + auto result = adjust_and_validate_format(std::move(s)); + song_list_format_dollar_free = remove_dollar_formatting(result); + return result; + })); + p.add("song_status_format", assign_default( + song_status_format, "{{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}", [this](std::string &&s) { + auto result = adjust_and_validate_format(std::move(s)); + if (result.find("$") != std::string::npos) + song_status_format_no_colors = stringToBuffer(result).str(); + else + song_status_format_no_colors = result; + return result; + })); + p.add("song_library_format", assign_default( + song_library_format, "{%n - }{%t}|{%f}", adjust_and_validate_format + )); + p.add("tag_editor_album_format", assign_default( + tag_editor_album_format, "{(%y) }%b", adjust_and_validate_format + )); + p.add("browser_sort_mode", assign_default( + browser_sort_mode, SortMode::Name + )); + p.add("browser_sort_format", assign_default( + browser_sort_format, "{%a - }{%t}|{%f} {(%l)}", adjust_and_validate_format + )); + p.add("alternative_header_first_line_format", assign_default( + new_header_first_line, "$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b", adjust_and_validate_format + )); + p.add("alternative_header_second_line_format", assign_default( + new_header_second_line, "{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}", adjust_and_validate_format + )); + p.add("now_playing_prefix", buffer( + now_playing_prefix, NC::Buffer(NC::Format::Bold), [this](NC::Buffer &&buf) { + now_playing_prefix_length = wideLength(ToWString(buf.str())); + return buf; + })); + p.add("now_playing_suffix", buffer( + now_playing_suffix, NC::Buffer(NC::Format::NoBold), [this](NC::Buffer &&buf) { + now_playing_suffix_length = wideLength(ToWString(buf.str())); + return buf; + })); + p.add("browser_playlist_prefix", buffer( + browser_playlist_prefix, NC::Buffer(NC::Color::Red, "playlist", NC::Color::End, ' '), id_() + )); + p.add("selected_item_prefix", buffer( + selected_item_prefix, NC::Buffer(NC::Color::Magenta), [this](NC::Buffer &&buf) { + selected_item_prefix_length = wideLength(ToWString(buf.str())); + return buf; + })); + p.add("selected_item_suffix", buffer( + selected_item_suffix, NC::Buffer(NC::Color::End), [this](NC::Buffer &&buf) { + selected_item_suffix_length = wideLength(ToWString(buf.str())); + return buf; + })); + p.add("modified_item_prefix", buffer( + modified_item_prefix, NC::Buffer(NC::Color::Green, "> ", NC::Color::End), id_() + )); + p.add("song_window_title_format", assign_default( + song_window_title_format, "{%a - }{%t}|{%f}", adjust_and_validate_format + )); + p.add("song_columns_list_format", assign_default( + columns_format, "(7f)[green]{l} (25)[cyan]{a} (40)[]{t|f} (30)[red]{b}", + [this](std::string &&v) { + boost::tie(columns, song_in_columns_to_string_format) = generate_columns(v); + return v; + })); + p.add("execute_on_song_change", assign_default( + execute_on_song_change, "" + )); + p.add("playlist_show_remaining_time", yes_no( + playlist_show_remaining_time, false + )); + p.add("playlist_shorten_total_times", yes_no( + playlist_shorten_total_times, false + )); + p.add("playlist_separate_albums", yes_no( + playlist_separate_albums, false + )); + p.add("playlist_display_mode", assign_default( + playlist_display_mode, DisplayMode::Classic + )); + p.add("browser_display_mode", assign_default( + browser_display_mode, DisplayMode::Classic + )); + p.add("search_engine_display_mode", assign_default( + search_engine_display_mode, DisplayMode::Classic + )); + p.add("playlist_editor_display_mode", assign_default( + playlist_editor_display_mode, DisplayMode::Classic + )); + p.add("discard_colors_if_item_is_selected", yes_no( + discard_colors_if_item_is_selected, true + )); + p.add("incremental_seeking", yes_no( + incremental_seeking, true + )); + p.add("seek_time", assign_default( + seek_time, 1 + )); + p.add("volume_change_step", assign_default( + volume_change_step, 1 + )); + p.add("autocenter_mode", yes_no( + titles_visibility, false + )); + p.add("centered_cursor", yes_no( + centered_cursor, false + )); + p.add("progressbar_look", assign_default( + progressbar, "=>", [](std::string &&s) { + auto result = ToWString(std::move(s)); + typedef std::wstring::size_type size_type; + boundsCheck(result.size(), size_type(2), size_type(3)); + // if two characters were specified, add third one (null) + result.resize(3); + return result; + })); + p.add("progressbar_boldness", yes_no( + progressbar_boldness, true + )); + p.add("default_place_to_search_in", option_parser::worker([this](std::string &&v) { + if (v == "database") + search_in_db = true; + else if (v == "playlist") + search_in_db = true; + else + throw std::runtime_error("invalid argument: " + v); + }, defaults_to(search_in_db, true) + )); + p.add("user_interface", assign_default( + design, Design::Classic + )); + p.add("media_library_primary_tag", option_parser::worker([this](std::string &&v) { + if (v == "artist") + media_lib_primary_tag = MPD_TAG_ARTIST; + else if (v == "date") + media_lib_primary_tag = MPD_TAG_DATE; + else if (v == "genre") + media_lib_primary_tag = MPD_TAG_GENRE; + else if (v == "composer") + media_lib_primary_tag = MPD_TAG_COMPOSER; + else if (v == "performer") + media_lib_primary_tag = MPD_TAG_PERFORMER; + else + throw std::runtime_error("invalid argument: " + v); + regex_type |= boost::regex::icase; + }, defaults_to(regex_type, boost::regex::literal | boost::regex::icase) + )); + p.add("default_find_mode", option_parser::worker([this](std::string &&v) { + if (v == "wrapped") + wrapped_search = true; + else if (v == "normal") + wrapped_search = false; + else + throw std::runtime_error("invalid argument: " + v); + }, defaults_to(wrapped_search, true) + )); + p.add("default_space_mode", option_parser::worker([this](std::string &&v) { + if (v == "add") + space_selects = false; + else if (v == "select") + space_selects = true; + else + throw std::runtime_error("invalid argument: " + v); + }, defaults_to(wrapped_search, true) + )); + p.add("default_tag_editor_pattern", assign_default( + pattern, "%n - %t" + )); + p.add("header_visibility", yes_no( + header_visibility, true + )); + p.add("statusbar_visibility", yes_no( + statusbar_visibility, true + )); + p.add("titles_visibility", yes_no( + titles_visibility, true + )); + p.add("header_text_scrolling", yes_no( + header_text_scrolling, true + )); + p.add("cyclic_scrolling", yes_no( + use_cyclic_scrolling, false + )); + p.add("lines_scrolled", assign_default( + lines_scrolled, 2 + )); + p.add("follow_now_playing_lyrics", yes_no( + now_playing_lyrics, false + )); + p.add("fetch_lyrics_for_current_song_in_background", yes_no( + fetch_lyrics_in_background, false + )); + p.add("store_lyrics_in_song_dir", yes_no( + store_lyrics_in_song_dir, false + )); + p.add("generate_win32_compatible_filenames", yes_no( + generate_win32_compatible_filenames, true + )); + p.add("allow_for_physical_item_deletion", yes_no( + allow_for_physical_item_deletion, false + )); + p.add("lastfm_preferred_language", assign_default( + lastfm_preferred_language, "en" + )); + p.add("space_add_mode", assign_default( + space_add_mode, SpaceAddMode::AlwaysAdd + )); + p.add("show_hidden_files_in_local_browser", yes_no( + local_browser_show_hidden_files, false + )); + p.add("screen_switcher_mode", option_parser::worker([this](std::string &&v) { + if (v == "previous") + screen_switcher_previous = true; + else + { + screen_switcher_previous = false; + boost::sregex_token_iterator i(v.begin(), v.end(), boost::regex("\\w+")), j; + for (; i != j; ++i) + { + auto screen = stringtoStartupScreenType(*i); + if (screen != ScreenType::Unknown) + screen_sequence.push_back(screen); + else + throw std::runtime_error("unknown screen: " + *i); + } + } + }, [this] { + screen_switcher_previous = false; + screen_sequence = { ScreenType::Playlist, ScreenType::Browser }; + })); + p.add("startup_screen", option_parser::worker([this](std::string &&v) { + startup_screen_type = stringtoStartupScreenType(v); + if (startup_screen_type == ScreenType::Unknown) + throw std::runtime_error("unknown screen: " + v); + }, defaults_to(startup_screen_type, ScreenType::Playlist) + )); + p.add("locked_screen_width_part", assign_default( + locked_screen_width_part, 50.0, [](double v) { + return v / 100; + })); + p.add("ask_for_locked_screen_width_part", yes_no( + ask_for_locked_screen_width_part, true + )); + p.add("jump_to_now_playing_song_at_start", yes_no( + jump_to_now_playing_song_at_start, true + )); + p.add("ask_before_clearing_playlists", yes_no( + ask_before_clearing_playlists, true + )); + p.add("clock_display_seconds", yes_no( + clock_display_seconds, false + )); + p.add("display_volume_level", yes_no( + display_volume_level, true + )); + p.add("display_bitrate", yes_no( + display_bitrate, false + )); + p.add("display_remaining_time", yes_no( + display_remaining_time, false + )); + p.add("regular_expressions", option_parser::worker([this](std::string &&v) { + if (v == "none") + regex_type = boost::regex::literal; + else if (v == "basic") + regex_type = boost::regex::basic; + else if (v == "extended") + regex_type = boost::regex::extended; + else + throw std::runtime_error("invalid argument: " + v); + regex_type |= boost::regex::icase; + }, defaults_to(regex_type, boost::regex::literal | boost::regex::icase) + )); + p.add("ignore_leading_the", yes_no( + ignore_leading_the, false + )); + p.add("block_search_constraints_change_if_items_found", yes_no( + block_search_constraints_change, true + )); + p.add("mouse_support", yes_no( + mouse_support, true + )); + p.add("mouse_list_scroll_whole_page", yes_no( + mouse_list_scroll_whole_page, true + )); + p.add("empty_tag_marker", assign_default( + empty_tag, "" + )); + p.add("tags_separator", assign_default( + tags_separator, " | " + )); + p.add("tag_editor_extended_numeration", yes_no( + tag_editor_extended_numeration, false + )); + p.add("media_library_sort_by_mtime", yes_no( + media_library_sort_by_mtime, false + )); + p.add("enable_window_title", yes_no( + set_window_title, true + )); + p.add("search_engine_default_search_mode", assign_default( + search_engine_default_search_mode, 1, [](unsigned v) { + boundsCheck(v, 1u, 3u); + return --v; + })); + p.add("external_editor", assign_default( + external_editor, "nano" + )); + p.add("use_console_editor", yes_no( + use_console_editor, true + )); + p.add("colors_enabled", yes_no( + colors_enabled, true + )); + p.add("empty_tag_color", assign_default( + empty_tags_color, NC::Color::Cyan + )); + p.add("header_window_color", assign_default( + header_color, NC::Color::Default + )); + p.add("volume_color", assign_default( + volume_color, NC::Color::Default + )); + p.add("state_line_color", assign_default( + state_line_color, NC::Color::Default + )); + p.add("state_flags_color", assign_default( + state_flags_color, NC::Color::Default + )); + p.add("main_window_color", assign_default( + main_color, NC::Color::Yellow + )); + p.add("color1", assign_default( + color1, NC::Color::White + )); + p.add("color2", assign_default( + color2, NC::Color::Green + )); + p.add("main_window_highlight_color", assign_default( + main_highlight_color, NC::Color::Yellow + )); + p.add("progressbar_color", assign_default( + progressbar_color, NC::Color::Default + )); + p.add("progressbar_elapsed_color", assign_default( + progressbar_elapsed_color, NC::Color::Default + )); + p.add("statusbar_color", assign_default( + statusbar_color, NC::Color::Default + )); + p.add("alternative_ui_separator_color", assign_default( + alternative_ui_separator_color, NC::Color::Black + )); + p.add("active_column_color", assign_default( + active_column_color, NC::Color::Red + )); + p.add("visualizer_color", assign_default( + visualizer_color, NC::Color::Yellow + )); + p.add("window_border_color", assign_default( + window_border, NC::Border::Green + )); + p.add("active_window_border", assign_default( + active_window_border, NC::Border::Red + )); + + std::ifstream f(config_path); + return p.run(f); } diff --git a/src/settings.h b/src/settings.h index 60bde129..88d92e91 100644 --- a/src/settings.h +++ b/src/settings.h @@ -26,12 +26,11 @@ #include #include #include -#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 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 screens_seq; + std::list 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 diff --git a/src/song.cpp b/src/song.cpp index 1168fed4..78874130 100644 --- a/src/song.cpp +++ b/src/song.cpp @@ -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(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, diff --git a/src/song.h b/src/song.h index 42d623e3..f97599fe 100644 --- a/src/song.h +++ b/src/song.h @@ -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; diff --git a/src/status.cpp b/src/status.cpp index 865a3b5e..a75fbd66 100644 --- a/src/status.cpp +++ b/src/status.cpp @@ -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(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(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(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(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 diff --git a/src/statusbar.cpp b/src/statusbar.cpp index 655c8011..6f4198c4 100644 --- a/src/statusbar.cpp +++ b/src/statusbar.cpp @@ -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(); } } diff --git a/src/strbuffer.h b/src/strbuffer.h index b043af66..7a23932f 100644 --- a/src/strbuffer.h +++ b/src/strbuffer.h @@ -63,18 +63,19 @@ template class BasicBuffer return m_id < rhs.m_id; } - friend Window &operator<<(Window &w, const Property &p) + template + 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 StringType; typedef std::set Properties; - BasicBuffer() { } - + template + BasicBuffer(Args... args) + { + construct(std::forward(args)...); + } + const StringType &str() const { return m_string; } const Properties &properties() const { return m_properties; } @@ -183,6 +188,14 @@ public: } private: + void construct() { } + template + void construct(ArgT &&arg, Args... args) + { + *this << std::forward(arg); + construct(std::forward(args)...); + } + StringType m_string; Properties m_properties; }; @@ -190,11 +203,11 @@ private: typedef BasicBuffer Buffer; typedef BasicBuffer WBuffer; -template -Window &operator<<(Window &w, const BasicBuffer &buffer) +template +OutputStreamT &operator<<(OutputStreamT &os, const BasicBuffer &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 &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; } } diff --git a/src/tag_editor.cpp b/src/tag_editor.cpp index 397da12c..9d9476b4 100644 --- a/src/tag_editor.cpp +++ b/src/tag_editor.cpp @@ -26,8 +26,8 @@ #include #include #include -#include +#include "actions.h" #include "browser.h" #include "charset.h" #include "display.h" diff --git a/src/title.cpp b/src/title.cpp index 7716d460..59853582 100644 --- a/src/title.cpp +++ b/src/title.cpp @@ -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(); } diff --git a/src/utility/comparators.cpp b/src/utility/comparators.cpp index de469932..35c5673d 100644 --- a/src/utility/comparators.cpp +++ b/src/utility/comparators.cpp @@ -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; diff --git a/src/utility/conversion.h b/src/utility/conversion.h index b5120f8c..0d417443 100644 --- a/src/utility/conversion.h +++ b/src/utility/conversion.h @@ -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 #include #include +#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 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 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 diff --git a/src/utility/functional.h b/src/utility/functional.h new file mode 100644 index 00000000..7b422cc1 --- /dev/null +++ b/src/utility/functional.h @@ -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 + +// identity function object +struct id_ +{ + template + constexpr auto operator()(ValueT &&v) const noexcept + -> decltype(std::forward(v)) + { + return std::forward(v); + } +}; + +#endif // NCMPCPP_UTILITY_FUNCTIONAL_H diff --git a/src/utility/option_parser.cpp b/src/utility/option_parser.cpp new file mode 100644 index 00000000..a1d12e6a --- /dev/null +++ b/src/utility/option_parser.cpp @@ -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 +#include + +#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)); +} diff --git a/src/utility/option_parser.h b/src/utility/option_parser.h new file mode 100644 index 00000000..6804d461 --- /dev/null +++ b/src/utility/option_parser.h @@ -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 +#include +#include +#include + +#include "helpers.h" +#include "strbuffer.h" +#include "utility/functional.h" + +struct option_parser +{ + typedef std::function parser_t; + typedef std::function default_t; + + struct worker + { + worker() { } + + template + worker(ParserT &&p, DefaultT &&d) + : m_defined(false), m_parser(std::forward(p)) + , m_default(std::forward(d)) { } + + template + void parse(ValueT &&value) + { + if (m_defined) + throw std::runtime_error("option already defined"); + m_parser(std::forward(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 + void add(const std::string &option, ParserT &&p, DefaultT &&d) + { + assert(m_parsers.count(option) == 0); + m_parsers[option] = worker(std::forward(p), std::forward(d)); + } + + template + void add(const std::string &option, WorkerT &&w) + { + assert(m_parsers.count(option) == 0); + m_parsers[option] = std::forward(w); + } + + bool run(std::istream &is); + +private: + std::unordered_map m_parsers; +}; + +template +option_parser::parser_t assign(ArgT &arg, TransformT map = id_()) +{ + return [&arg, map](std::string &&v) { + try { + arg = map(boost::lexical_cast(v)); + } catch (boost::bad_lexical_cast &) { + throw std::runtime_error("invalid value: " + v); + } + }; +} + +template +option_parser::default_t defaults_to(ArgT &arg, ValueT &&value) +{ + return [&arg, value] { + arg = std::move(value); + }; +} + +template +option_parser::worker assign_default(ArgT &arg, ValueT &&value, TransformT map) +{ + return option_parser::worker( + assign(arg, map), defaults_to(arg, map(std::forward(value))) + ); +} + +template +option_parser::worker assign_default(ArgT &arg, ValueT &&value) +{ + return assign_default(arg, std::forward(value), id_()); +} + +// workers for specific types + +template +option_parser::worker buffer(NC::Buffer &arg, ValueT &&value, TransformT map) +{ + return option_parser::worker(assign(arg, [&arg, map](std::string &&s) { + return map(stringToBuffer(s)); + }), defaults_to(arg, map(std::forward(value)))); +} + +option_parser::worker yes_no(bool &arg, bool value); + +#endif // NCMPCPP_UTILITY_OPTION_PARSER_H diff --git a/src/window.cpp b/src/window.cpp index 41f1f81e..65e69a2d 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -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[] = diff --git a/src/window.h b/src/window.h index cff4e5a2..144b9e9e 100644 --- a/src/window.h +++ b/src/window.h @@ -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()