actions: use unique_ptr for storing actions
[ncmpcpp.git] / src / settings.cpp
blobf1024e1b4512d0333ee75e7fcef1343dccfdd727
1 /***************************************************************************
2 * Copyright (C) 2008-2016 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include <boost/tuple/tuple.hpp>
22 #include <boost/tokenizer.hpp>
23 #include <fstream>
24 #include <stdexcept>
26 #include "configuration.h"
27 #include "format_impl.h"
28 #include "helpers.h"
29 #include "settings.h"
30 #include "utility/conversion.h"
31 #include "utility/option_parser.h"
32 #include "utility/type_conversions.h"
34 #ifdef HAVE_LANGINFO_H
35 # include <langinfo.h>
36 #endif
38 Configuration Config;
40 namespace {
42 std::vector<Column> generate_columns(const std::string &format)
44 std::vector<Column> result;
45 std::string width;
46 size_t pos = 0;
47 while (!(width = getEnclosedString(format, '(', ')', &pos)).empty())
49 Column col;
50 auto scolor = getEnclosedString(format, '[', ']', &pos);
51 if (scolor.empty())
52 col.color = NC::Color::Default;
53 else
54 col.color = boost::lexical_cast<NC::Color>(scolor);
56 if (*width.rbegin() == 'f')
58 col.fixed = true;
59 width.resize(width.size()-1);
61 else
62 col.fixed = false;
64 auto tag_type = getEnclosedString(format, '{', '}', &pos);
65 // alternative name
66 size_t tag_type_colon_pos = tag_type.find(':');
67 if (tag_type_colon_pos != std::string::npos)
69 col.name = ToWString(tag_type.substr(tag_type_colon_pos+1));
70 tag_type.resize(tag_type_colon_pos);
73 if (!tag_type.empty())
75 size_t i = -1;
77 // extract tag types in format a|b|c etc.
79 col.type += tag_type[(++i)++]; // nice one.
80 while (tag_type[i] == '|');
82 // apply attributes
83 for (; i < tag_type.length(); ++i)
85 switch (tag_type[i])
87 case 'r':
88 col.right_alignment = 1;
89 break;
90 case 'E':
91 col.display_empty_tag = 0;
92 break;
96 else // empty column
97 col.display_empty_tag = 0;
99 col.width = boost::lexical_cast<int>(width);
100 result.push_back(col);
103 // calculate which column is the last one to have relative width and stretch it accordingly
104 if (!result.empty())
106 int stretch_limit = 0;
107 auto it = result.rbegin();
108 for (; it != result.rend(); ++it)
110 if (it->fixed)
111 stretch_limit += it->width;
112 else
113 break;
115 // if it's false, all columns are fixed
116 if (it != result.rend())
117 it->stretch_limit = stretch_limit;
120 return result;
123 Format::AST<char> columns_to_format(const std::vector<Column> &columns)
125 std::vector<Format::Expression<char>> result;
127 auto column = columns.begin();
128 while (true)
130 Format::FirstOf<char> first_of;
131 for (const auto &type : column->type)
133 auto f = charToGetFunction(type);
134 assert(f != nullptr);
135 first_of.base().push_back(f);
137 result.push_back(std::move(first_of));
139 if (++column != columns.end())
140 result.push_back(" ");
141 else
142 break;
145 return Format::AST<char>(std::move(result));
148 void add_slash_at_the_end(std::string &s)
150 if (s.empty() || *s.rbegin() != '/')
152 s.resize(s.size()+1);
153 *s.rbegin() = '/';
157 std::string adjust_directory(std::string s)
159 add_slash_at_the_end(s);
160 expand_home(s);
161 return s;
164 std::string adjust_path(std::string s)
166 expand_home(s);
167 return s;
170 NC::Buffer buffer(const std::string &v)
172 NC::Buffer result;
173 Format::print(
174 Format::parse(v, Format::Flags::Color | Format::Flags::Format),
175 result,
176 nullptr);
177 return result;
181 void deprecated(const char *option, double version_removal)
183 std::cerr << "WARNING: " << option
184 << " is deprecated and will be removed in " << version_removal << "\n";
190 bool Configuration::read(const std::vector<std::string> &config_paths, bool ignore_errors)
192 option_parser p;
194 // keep the same order of variables as in configuration file
195 p.add("ncmpcpp_directory", &ncmpcpp_directory, "~/.ncmpcpp/", adjust_directory);
196 p.add("lyrics_directory", &lyrics_directory, "~/.lyrics/", adjust_directory);
197 p.add<void>("mpd_host", nullptr, "localhost", [](std::string host) {
198 expand_home(host);
199 Mpd.SetHostname(host);
201 p.add<void>("mpd_port", nullptr, "6600", [](std::string port) {
202 Mpd.SetPort(verbose_lexical_cast<unsigned>(port));
204 p.add("mpd_music_dir", &mpd_music_dir, "~/music", adjust_directory);
205 p.add("mpd_connection_timeout", &mpd_connection_timeout, "5");
206 p.add("mpd_crossfade_time", &crossfade_time, "5");
207 p.add("visualizer_fifo_path", &visualizer_fifo_path, "/tmp/mpd.fifo", adjust_path);
208 p.add("visualizer_output_name", &visualizer_output_name, "Visualizer feed");
209 p.add("visualizer_in_stereo", &visualizer_in_stereo, "yes", yes_no);
210 p.add("visualizer_sample_multiplier", &visualizer_sample_multiplier, "1",
211 [](std::string v) {
212 double multiplier = verbose_lexical_cast<double>(v);
213 lowerBoundCheck(multiplier, 0.0);
214 return multiplier;
216 p.add("visualizer_sync_interval", &visualizer_sync_interval, "30",
217 [](std::string v) {
218 unsigned sync_interval = verbose_lexical_cast<unsigned>(v);
219 lowerBoundCheck<unsigned>(sync_interval, 10);
220 return boost::posix_time::seconds(sync_interval);
222 p.add("visualizer_type", &visualizer_type, "wave");
223 p.add("visualizer_look", &visualizer_chars, "●▮", [](std::string s) {
224 auto result = ToWString(std::move(s));
225 boundsCheck<std::wstring::size_type>(result.size(), 2, 2);
226 return result;
228 p.add("visualizer_color", &visualizer_colors,
229 "blue, cyan, green, yellow, magenta, red", list_of<NC::Color>);
230 p.add("system_encoding", &system_encoding, "", [](std::string encoding) {
231 #ifdef HAVE_LANGINFO_H
232 // try to autodetect system encoding
233 if (encoding.empty())
235 encoding = nl_langinfo(CODESET);
236 if (encoding == "UTF-8") // mpd uses utf-8 by default so no need to convert
237 encoding.clear();
239 #endif // HAVE_LANGINFO_H
240 return encoding;
242 p.add("playlist_disable_highlight_delay", &playlist_disable_highlight_delay,
243 "5", [](std::string v) {
244 return boost::posix_time::seconds(verbose_lexical_cast<unsigned>(v));
246 p.add("message_delay_time", &message_delay_time, "5");
247 p.add("song_list_format", &song_list_format,
248 "{%a - }{%t}|{$8%f$9}$R{$3(%l)$9}", [](std::string v) {
249 return Format::parse(v);
251 p.add("song_status_format", &song_status_format,
252 "{{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}", [this](std::string v) {
253 auto flags = Format::Flags::All ^ Format::Flags::OutputSwitch;
254 // precompute wide format for status display
255 song_status_wformat = Format::parse(ToWString(v), flags);
256 return Format::parse(v, flags);
258 p.add("song_library_format", &song_library_format,
259 "{%n - }{%t}|{%f}", [](std::string v) {
260 return Format::parse(v);
262 p.add("alternative_header_first_line_format", &new_header_first_line,
263 "$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b", [](std::string v) {
264 return Format::parse(ToWString(std::move(v)),
265 Format::Flags::All ^ Format::Flags::OutputSwitch);
267 p.add("alternative_header_second_line_format", &new_header_second_line,
268 "{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}", [](std::string v) {
269 return Format::parse(ToWString(std::move(v)),
270 Format::Flags::All ^ Format::Flags::OutputSwitch);
272 p.add("now_playing_prefix", &now_playing_prefix,
273 "$b", [this](std::string v) {
274 NC::Buffer result = buffer(v);
275 now_playing_prefix_length = wideLength(ToWString(result.str()));
276 return result;
278 p.add("now_playing_suffix", &now_playing_suffix,
279 "$/b", [this](std::string v) {
280 NC::Buffer result = buffer(v);
281 now_playing_suffix_length = wideLength(ToWString(result.str()));
282 return result;
284 p.add("browser_playlist_prefix", &browser_playlist_prefix, "$2playlist$9 ", buffer);
285 p.add("selected_item_prefix", &selected_item_prefix,
286 "$6", [this](std::string v) {
287 NC::Buffer result = buffer(v);
288 selected_item_prefix_length = wideLength(ToWString(result.str()));
289 return result;
291 p.add("selected_item_suffix", &selected_item_suffix,
292 "$9", [this](std::string v) {
293 NC::Buffer result = buffer(v);
294 selected_item_suffix_length = wideLength(ToWString(result.str()));
295 return result;
297 p.add("modified_item_prefix", &modified_item_prefix, "$3>$9 ", buffer);
298 p.add("song_window_title_format", &song_window_title_format,
299 "{%a - }{%t}|{%f}", [](std::string v) {
300 return Format::parse(v, Format::Flags::Tag);
302 p.add("browser_sort_mode", &browser_sort_mode, "name");
303 p.add("browser_sort_format", &browser_sort_format,
304 "{%a - }{%t}|{%f} {(%l)}", [](std::string v) {
305 return Format::parse(v, Format::Flags::Tag);
307 p.add("song_columns_list_format", &song_columns_mode_format,
308 "(20)[]{a} (6f)[green]{NE} (50)[white]{t|f:Title} (20)[cyan]{b} (7f)[magenta]{l}",
309 [this](std::string v) {
310 columns = generate_columns(v);
311 return columns_to_format(columns);
313 p.add("execute_on_song_change", &execute_on_song_change, "", adjust_path);
314 p.add("execute_on_player_state_change", &execute_on_player_state_change,
315 "", adjust_path);
316 p.add("playlist_show_mpd_host", &playlist_show_mpd_host, "no", yes_no);
317 p.add("playlist_show_remaining_time", &playlist_show_remaining_time, "no", yes_no);
318 p.add("playlist_shorten_total_times", &playlist_shorten_total_times, "no", yes_no);
319 p.add("playlist_separate_albums", &playlist_separate_albums, "no", yes_no);
320 p.add("playlist_display_mode", &playlist_display_mode, "columns");
321 p.add("browser_display_mode", &browser_display_mode, "classic");
322 p.add("search_engine_display_mode", &search_engine_display_mode, "classic");
323 p.add("playlist_editor_display_mode", &playlist_editor_display_mode, "classic");
324 p.add("discard_colors_if_item_is_selected", &discard_colors_if_item_is_selected,
325 "yes", yes_no);
326 p.add("show_duplicate_tags", &MPD::Song::ShowDuplicateTags, "yes", yes_no);
327 p.add("incremental_seeking", &incremental_seeking, "yes", yes_no);
328 p.add("seek_time", &seek_time, "1");
329 p.add("volume_change_step", &volume_change_step, "2");
330 p.add("autocenter_mode", &autocenter_mode, "no", yes_no);
331 p.add("centered_cursor", &centered_cursor, "no", yes_no);
332 p.add("progressbar_look", &progressbar, "=>", [](std::string v) {
333 auto result = ToWString(std::move(v));
334 boundsCheck<std::wstring::size_type>(result.size(), 2, 3);
335 // If two characters were specified, fill \0 as the third one.
336 result.resize(3);
337 return result;
339 p.add("progressbar_boldness", &progressbar_boldness, "yes", yes_no);
340 p.add("default_place_to_search_in", &search_in_db, "database", [](std::string v) {
341 if (v == "database")
342 return true;
343 else if (v == "playlist")
344 return false;
345 else
346 invalid_value(v);
348 p.add("user_interface", &design, "classic");
349 p.add("data_fetching_delay", &data_fetching_delay, "yes", yes_no);
350 p.add("media_library_primary_tag", &media_lib_primary_tag, "artist", [](std::string v) {
351 if (v == "artist")
352 return MPD_TAG_ARTIST;
353 else if (v == "album_artist")
354 return MPD_TAG_ALBUM_ARTIST;
355 else if (v == "date")
356 return MPD_TAG_DATE;
357 else if (v == "genre")
358 return MPD_TAG_GENRE;
359 else if (v == "composer")
360 return MPD_TAG_COMPOSER;
361 else if (v == "performer")
362 return MPD_TAG_PERFORMER;
363 else
364 invalid_value(v);
366 p.add("default_find_mode", &wrapped_search, "wrapped", [](std::string v) {
367 if (v == "wrapped")
368 return true;
369 else if (v == "normal")
370 return false;
371 else
372 invalid_value(v);
374 p.add("default_tag_editor_pattern", &pattern, "%n - %t");
375 p.add("header_visibility", &header_visibility, "yes", yes_no);
376 p.add("statusbar_visibility", &statusbar_visibility, "yes", yes_no);
377 p.add("titles_visibility", &titles_visibility, "yes", yes_no);
378 p.add("header_text_scrolling", &header_text_scrolling, "yes", yes_no);
379 p.add("cyclic_scrolling", &use_cyclic_scrolling, "no", yes_no);
380 p.add("lines_scrolled", &lines_scrolled, "2");
381 p.add("lyrics_fetchers", &lyrics_fetchers,
382 "lyricwiki, azlyrics, genius, sing365, lyricsmania, metrolyrics, justsomelyrics, tekstowo, internet",
383 list_of<LyricsFetcher_>);
384 p.add("follow_now_playing_lyrics", &now_playing_lyrics, "no", yes_no);
385 p.add("fetch_lyrics_for_current_song_in_background", &fetch_lyrics_in_background,
386 "no", yes_no);
387 p.add("store_lyrics_in_song_dir", &store_lyrics_in_song_dir, "no", yes_no);
388 p.add("generate_win32_compatible_filenames", &generate_win32_compatible_filenames,
389 "yes", yes_no);
390 p.add("allow_for_physical_item_deletion", &allow_for_physical_item_deletion,
391 "no", yes_no);
392 p.add("lastfm_preferred_language", &lastfm_preferred_language, "en");
393 p.add("space_add_mode", &space_add_mode, "add_remove");
394 p.add("show_hidden_files_in_local_browser", &local_browser_show_hidden_files,
395 "no", yes_no);
396 p.add<void>(
397 "screen_switcher_mode", nullptr, "playlist, browser",
398 [this](std::string v) {
399 if (v == "previous")
400 screen_switcher_previous = true;
401 else
403 screen_switcher_previous = false;
404 screen_sequence = list_of<ScreenType>(v, [](std::string s) {
405 auto screen = stringtoStartupScreenType(s);
406 if (screen == ScreenType::Unknown)
407 invalid_value(s);
408 return screen;
412 p.add("startup_screen", &startup_screen_type, "playlist", [](std::string v) {
413 auto screen = stringtoStartupScreenType(v);
414 if (screen == ScreenType::Unknown)
415 invalid_value(v);
416 return screen;
418 p.add("startup_slave_screen", &startup_slave_screen_type, "", [](std::string v) {
419 boost::optional<ScreenType> screen;
420 if (!v.empty())
422 screen = stringtoStartupScreenType(v);
423 if (screen == ScreenType::Unknown)
424 invalid_value(v);
426 return screen;
428 p.add("startup_slave_screen_focus", &startup_slave_screen_focus, "no", yes_no);
429 p.add("locked_screen_width_part", &locked_screen_width_part,
430 "50", [](std::string v) {
431 return verbose_lexical_cast<double>(v) / 100;
433 p.add("ask_for_locked_screen_width_part", &ask_for_locked_screen_width_part,
434 "yes", yes_no);
435 p.add("jump_to_now_playing_song_at_start", &jump_to_now_playing_song_at_start,
436 "yes", yes_no);
437 p.add("ask_before_clearing_playlists", &ask_before_clearing_playlists,
438 "yes", yes_no);
439 p.add("ask_before_shuffling_playlists", &ask_before_clearing_playlists,
440 "yes", yes_no);
441 p.add("clock_display_seconds", &clock_display_seconds, "no", yes_no);
442 p.add("display_volume_level", &display_volume_level, "yes", yes_no);
443 p.add("display_bitrate", &display_bitrate, "no", yes_no);
444 p.add("display_remaining_time", &display_remaining_time, "no", yes_no);
445 p.add("regular_expressions", &regex_type, "perl", [](std::string v) {
446 if (v == "none")
447 return boost::regex::icase | boost::regex::literal;
448 else if (v == "basic")
449 return boost::regex::icase | boost::regex::basic;
450 else if (v == "extended")
451 return boost::regex::icase | boost::regex::extended;
452 else if (v == "perl")
453 return boost::regex::icase | boost::regex::perl;
454 else
455 invalid_value(v);
457 p.add("ignore_leading_the", &ignore_leading_the, "no", yes_no);
458 p.add("block_search_constraints_change_if_items_found",
459 &block_search_constraints_change, "yes", yes_no);
460 p.add("mouse_support", &mouse_support, "yes", yes_no);
461 p.add("mouse_list_scroll_whole_page", &mouse_list_scroll_whole_page, "yes", yes_no);
462 p.add("empty_tag_marker", &empty_tag, "<empty>");
463 p.add("tags_separator", &MPD::Song::TagsSeparator, " | ");
464 p.add("tag_editor_extended_numeration", &tag_editor_extended_numeration, "no", yes_no);
465 p.add("media_library_sort_by_mtime", &media_library_sort_by_mtime, "no", yes_no);
466 p.add("enable_window_title", &set_window_title, "yes", [](std::string v) {
467 // Consider this variable only if TERM variable is available and we're not
468 // in emacs terminal nor tty (through any wrapper like screen).
469 auto term = getenv("TERM");
470 if (term != nullptr
471 && strstr(term, "linux") == nullptr
472 && strncmp(term, "eterm", const_strlen("eterm")))
473 return yes_no(v);
474 else
476 std::clog << "Terminal doesn't support window title, skipping 'enable_window_title'.\n";
477 return false;
480 p.add("search_engine_default_search_mode", &search_engine_default_search_mode,
481 "1", [](std::string v) {
482 auto mode = verbose_lexical_cast<unsigned>(v);
483 boundsCheck<unsigned>(mode, 1, 3);
484 return --mode;
486 p.add("external_editor", &external_editor, "nano", adjust_path);
487 p.add("use_console_editor", &use_console_editor, "yes", yes_no);
488 p.add("colors_enabled", &colors_enabled, "yes", yes_no);
489 p.add("empty_tag_color", &empty_tags_color, "cyan");
490 p.add("header_window_color", &header_color, "default");
491 p.add("volume_color", &volume_color, "default");
492 p.add("state_line_color", &state_line_color, "default");
493 p.add("state_flags_color", &state_flags_color, "default");
494 p.add("main_window_color", &main_color, "yellow");
495 p.add("color1", &color1, "white");
496 p.add("color2", &color2, "green");
497 p.add("main_window_highlight_color", &main_highlight_color, "yellow");
498 p.add("progressbar_color", &progressbar_color, "black");
499 p.add("progressbar_elapsed_color", &progressbar_elapsed_color, "green");
500 p.add("statusbar_color", &statusbar_color, "default");
501 p.add("alternative_ui_separator_color", &alternative_ui_separator_color, "black");
502 p.add("active_column_color", &active_column_color, "red");
503 p.add("window_border_color", &window_border, "green", verbose_lexical_cast<NC::Color>);
504 p.add("active_window_border", &active_window_border, "red",
505 verbose_lexical_cast<NC::Color>);
507 return std::all_of(
508 config_paths.begin(),
509 config_paths.end(),
510 [&](const std::string &config_path) {
511 std::ifstream f(config_path);
512 if (f.is_open())
513 std::clog << "Reading configuration from " << config_path << "...\n";
514 return p.run(f, ignore_errors);
516 ) && p.initialize_undefined(ignore_errors);