settings: make possible values of visualizer_type reflect the ones in config file
[ncmpcpp.git] / src / settings.cpp
blob0d03c3fdc7312ff736f8dda6e78dceae5c0455be
1 /***************************************************************************
2 * Copyright (C) 2008-2014 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 <fstream>
23 #include <stdexcept>
25 #include "configuration.h"
26 #include "helpers.h"
27 #include "settings.h"
28 #include "utility/conversion.h"
29 #include "utility/option_parser.h"
30 #include "utility/type_conversions.h"
32 #ifdef HAVE_LANGINFO_H
33 # include <langinfo.h>
34 #endif
36 Configuration Config;
38 namespace {
40 std::string remove_dollar_formatting(const std::string &s)
42 std::string result;
43 for (size_t i = 0; i < s.size(); ++i)
45 if (s[i] != '$')
46 result += s[i];
47 else
48 ++i;
50 return result;
53 std::pair<std::vector<Column>, std::string> generate_columns(const std::string &format)
55 std::vector<Column> result;
56 std::string width;
57 size_t pos = 0;
58 while (!(width = getEnclosedString(format, '(', ')', &pos)).empty())
60 Column col;
61 col.color = stringToColor(getEnclosedString(format, '[', ']', &pos));
62 std::string tag_type = getEnclosedString(format, '{', '}', &pos);
64 if (*width.rbegin() == 'f')
66 col.fixed = true;
67 width.resize(width.size()-1);
69 else
70 col.fixed = false;
72 // alternative name
73 size_t tag_type_colon_pos = tag_type.find(':');
74 if (tag_type_colon_pos != std::string::npos)
76 col.name = ToWString(tag_type.substr(tag_type_colon_pos+1));
77 tag_type.resize(tag_type_colon_pos);
80 if (!tag_type.empty())
82 size_t i = -1;
84 // extract tag types in format a|b|c etc.
86 col.type += tag_type[(++i)++]; // nice one.
87 while (tag_type[i] == '|');
89 // apply attributes
90 for (; i < tag_type.length(); ++i)
92 switch (tag_type[i])
94 case 'r':
95 col.right_alignment = 1;
96 break;
97 case 'E':
98 col.display_empty_tag = 0;
99 break;
103 else // empty column
104 col.display_empty_tag = 0;
106 col.width = boost::lexical_cast<int>(width);
107 result.push_back(col);
110 // calculate which column is the last one to have relative width and stretch it accordingly
111 if (!result.empty())
113 int stretch_limit = 0;
114 auto it = result.rbegin();
115 for (; it != result.rend(); ++it)
117 if (it->fixed)
118 stretch_limit += it->width;
119 else
120 break;
122 // if it's false, all columns are fixed
123 if (it != result.rend())
124 it->stretch_limit = stretch_limit;
127 std::string string_format;
128 // generate format for converting tags in columns to string for Playlist::SongInColumnsToString()
129 char tag[] = "{% }|";
130 string_format = "{";
131 for (auto it = result.begin(); it != result.end(); ++it)
133 for (std::string::const_iterator j = it->type.begin(); j != it->type.end(); ++j)
135 tag[2] = *j;
136 string_format += tag;
138 *string_format.rbegin() = ' ';
140 if (string_format.length() == 1) // only '{'
141 string_format += '}';
142 else
143 *string_format.rbegin() = '}';
145 return std::make_pair(std::move(result), std::move(string_format));
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_and_validate_format(std::string &&format)
166 MPD::Song::validateFormat(format);
167 format = "{" + format + "}";
168 return format;
171 // parser worker for buffer
172 template <typename ValueT, typename TransformT>
173 option_parser::worker buffer(NC::Buffer &arg, ValueT &&value, TransformT &&map)
175 return option_parser::worker(assign<std::string>(arg, [&arg, map](std::string &&s) {
176 return map(stringToBuffer(s));
177 }), defaults_to(arg, map(std::forward<ValueT>(value))));
182 bool Configuration::read(const std::string &config_path)
184 std::string mpd_host;
185 unsigned mpd_port;
186 std::string columns_format;
188 // keep the same order of variables as in configuration file
189 option_parser p;
190 p.add("ncmpcpp_directory", assign_default<std::string>(
191 ncmpcpp_directory, "~/.ncmpcpp/", adjust_directory
193 p.add("lyrics_directory", assign_default<std::string>(
194 lyrics_directory, "~/.lyrics/", adjust_directory
196 p.add("mpd_host", assign_default<std::string>(
197 mpd_host, "localhost", [](std::string &&host) {
198 Mpd.SetHostname(host);
199 return host;
200 }));
201 p.add("mpd_port", assign_default<unsigned>(
202 mpd_port, 6600, [](unsigned port) {
203 Mpd.SetPort(port);
204 return port;
205 }));
206 p.add("mpd_music_dir", assign_default<std::string>(
207 mpd_music_dir, "~/music", adjust_directory
209 p.add("mpd_connection_timeout", assign_default(
210 mpd_connection_timeout, 5
212 p.add("mpd_crossfade_time", assign_default(
213 crossfade_time, 5
215 p.add("visualizer_fifo_path", assign_default(
216 visualizer_fifo_path, "/tmp/mpd.fifo"
218 p.add("visualizer_output_name", assign_default(
219 visualizer_output_name, "Visualizer feed"
221 p.add("visualizer_in_stereo", yes_no(
222 visualizer_in_stereo, true
224 p.add("visualizer_sample_multiplier", assign_default<double>(
225 visualizer_sample_multiplier, 1.0, [](double v) {
226 lowerBoundCheck(v, 1.0);
227 return v;
228 }));
229 p.add("visualizer_sync_interval", assign_default<unsigned>(
230 visualizer_sync_interval, 30, [](unsigned v) {
231 return boost::posix_time::seconds(v);
232 }));
233 p.add("visualizer_type", option_parser::worker([this](std::string &&v) {
234 if (v == "wave")
235 visualizer_use_wave = true;
236 else if (v == "spectrum")
237 visualizer_use_wave = false;
238 else
239 throw std::runtime_error("invalid argument: " + v);
240 }, defaults_to(visualizer_use_wave, true)
242 p.add("visualizer_look", assign_default<std::string>(
243 visualizer_chars, "●▮", [](std::string &&s) {
244 auto result = ToWString(std::move(s));
245 typedef std::wstring::size_type size_type;
246 boundsCheck(result.size(), size_type(2), size_type(2));
247 return result;
248 }));
249 p.add("system_encoding", assign_default<std::string>(
250 system_encoding, "", [](std::string &&enc) {
251 # ifdef HAVE_LANGINFO_H
252 // try to autodetect system encoding
253 if (enc.empty())
255 enc = nl_langinfo(CODESET);
256 if (enc == "UTF-8") // mpd uses utf-8 by default so no need to convert
257 enc.clear();
259 # endif // HAVE_LANGINFO_H
260 return enc;
261 }));
262 p.add("playlist_disable_highlight_delay", assign_default<unsigned>(
263 playlist_disable_highlight_delay, 5, [](unsigned v) {
264 return boost::posix_time::seconds(v);
265 }));
266 p.add("message_delay_time", assign_default(
267 message_delay_time, 5
269 p.add("song_list_format", assign_default<std::string>(
270 song_list_format, "{%a - }{%t}|{$8%f$9}$R{$3(%l)$9}", [this](std::string &&s) {
271 auto result = adjust_and_validate_format(std::move(s));
272 song_list_format_dollar_free = remove_dollar_formatting(result);
273 return result;
274 }));
275 p.add("song_status_format", assign_default<std::string>(
276 song_status_format, "{{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}", [this](std::string &&s) {
277 auto result = adjust_and_validate_format(std::move(s));
278 if (result.find("$") != std::string::npos)
279 song_status_format_no_colors = stringToBuffer(result).str();
280 else
281 song_status_format_no_colors = result;
282 return result;
283 }));
284 p.add("song_library_format", assign_default<std::string>(
285 song_library_format, "{%n - }{%t}|{%f}", adjust_and_validate_format
287 p.add("tag_editor_album_format", assign_default<std::string>(
288 tag_editor_album_format, "{(%y) }%b", adjust_and_validate_format
290 p.add("browser_sort_mode", assign_default(
291 browser_sort_mode, SortMode::Name
293 p.add("browser_sort_format", assign_default<std::string>(
294 browser_sort_format, "{%a - }{%t}|{%f} {(%l)}", adjust_and_validate_format
296 p.add("alternative_header_first_line_format", assign_default<std::string>(
297 new_header_first_line, "$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b", adjust_and_validate_format
299 p.add("alternative_header_second_line_format", assign_default<std::string>(
300 new_header_second_line, "{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}", adjust_and_validate_format
302 p.add("now_playing_prefix", buffer(
303 now_playing_prefix, NC::Buffer(NC::Format::Bold), [this](NC::Buffer &&buf) {
304 now_playing_prefix_length = wideLength(ToWString(buf.str()));
305 return buf;
306 }));
307 p.add("now_playing_suffix", buffer(
308 now_playing_suffix, NC::Buffer(NC::Format::NoBold), [this](NC::Buffer &&buf) {
309 now_playing_suffix_length = wideLength(ToWString(buf.str()));
310 return buf;
311 }));
312 p.add("browser_playlist_prefix", buffer(
313 browser_playlist_prefix, NC::Buffer(NC::Color::Red, "playlist", NC::Color::End, ' '), id_()
315 p.add("selected_item_prefix", buffer(
316 selected_item_prefix, NC::Buffer(NC::Color::Magenta), [this](NC::Buffer &&buf) {
317 selected_item_prefix_length = wideLength(ToWString(buf.str()));
318 return buf;
319 }));
320 p.add("selected_item_suffix", buffer(
321 selected_item_suffix, NC::Buffer(NC::Color::End), [this](NC::Buffer &&buf) {
322 selected_item_suffix_length = wideLength(ToWString(buf.str()));
323 return buf;
324 }));
325 p.add("modified_item_prefix", buffer(
326 modified_item_prefix, NC::Buffer(NC::Color::Green, "> ", NC::Color::End), id_()
328 p.add("song_window_title_format", assign_default<std::string>(
329 song_window_title_format, "{%a - }{%t}|{%f}", adjust_and_validate_format
331 p.add("song_columns_list_format", assign_default<std::string>(
332 columns_format, "(20)[]{a} (6f)[green]{NE} (50)[white]{t|f:Title} (20)[cyan]{b} (7f)[magenta]{l}",
333 [this](std::string &&v) {
334 boost::tie(columns, song_in_columns_to_string_format) = generate_columns(v);
335 return v;
336 }));
337 p.add("execute_on_song_change", assign_default(
338 execute_on_song_change, ""
340 p.add("playlist_show_remaining_time", yes_no(
341 playlist_show_remaining_time, false
343 p.add("playlist_shorten_total_times", yes_no(
344 playlist_shorten_total_times, false
346 p.add("playlist_separate_albums", yes_no(
347 playlist_separate_albums, false
349 p.add("playlist_display_mode", assign_default(
350 playlist_display_mode, DisplayMode::Columns
352 p.add("browser_display_mode", assign_default(
353 browser_display_mode, DisplayMode::Classic
355 p.add("search_engine_display_mode", assign_default(
356 search_engine_display_mode, DisplayMode::Classic
358 p.add("playlist_editor_display_mode", assign_default(
359 playlist_editor_display_mode, DisplayMode::Classic
361 p.add("discard_colors_if_item_is_selected", yes_no(
362 discard_colors_if_item_is_selected, true
364 p.add("incremental_seeking", yes_no(
365 incremental_seeking, true
367 p.add("seek_time", assign_default(
368 seek_time, 1
370 p.add("volume_change_step", assign_default(
371 volume_change_step, 2
373 p.add("autocenter_mode", yes_no(
374 titles_visibility, false
376 p.add("centered_cursor", yes_no(
377 centered_cursor, false
379 p.add("progressbar_look", assign_default<std::string>(
380 progressbar, "=>", [](std::string &&s) {
381 auto result = ToWString(std::move(s));
382 typedef std::wstring::size_type size_type;
383 boundsCheck(result.size(), size_type(2), size_type(3));
384 // if two characters were specified, add third one (null)
385 result.resize(3);
386 return result;
387 }));
388 p.add("progressbar_boldness", yes_no(
389 progressbar_boldness, true
391 p.add("default_place_to_search_in", option_parser::worker([this](std::string &&v) {
392 if (v == "database")
393 search_in_db = true;
394 else if (v == "playlist")
395 search_in_db = true;
396 else
397 throw std::runtime_error("invalid argument: " + v);
398 }, defaults_to(search_in_db, true)
400 p.add("user_interface", assign_default(
401 design, Design::Classic
403 p.add("media_library_primary_tag", option_parser::worker([this](std::string &&v) {
404 if (v == "artist")
405 media_lib_primary_tag = MPD_TAG_ARTIST;
406 else if (v == "album_artist")
407 media_lib_primary_tag = MPD_TAG_ALBUM_ARTIST;
408 else if (v == "date")
409 media_lib_primary_tag = MPD_TAG_DATE;
410 else if (v == "genre")
411 media_lib_primary_tag = MPD_TAG_GENRE;
412 else if (v == "composer")
413 media_lib_primary_tag = MPD_TAG_COMPOSER;
414 else if (v == "performer")
415 media_lib_primary_tag = MPD_TAG_PERFORMER;
416 else
417 throw std::runtime_error("invalid argument: " + v);
418 regex_type |= boost::regex::icase;
419 }, defaults_to(regex_type, boost::regex::literal | boost::regex::icase)
421 p.add("default_find_mode", option_parser::worker([this](std::string &&v) {
422 if (v == "wrapped")
423 wrapped_search = true;
424 else if (v == "normal")
425 wrapped_search = false;
426 else
427 throw std::runtime_error("invalid argument: " + v);
428 }, defaults_to(wrapped_search, true)
430 p.add("default_space_mode", option_parser::worker([this](std::string &&v) {
431 if (v == "add")
432 space_selects = false;
433 else if (v == "select")
434 space_selects = true;
435 else
436 throw std::runtime_error("invalid argument: " + v);
437 }, defaults_to(wrapped_search, true)
439 p.add("default_tag_editor_pattern", assign_default(
440 pattern, "%n - %t"
442 p.add("header_visibility", yes_no(
443 header_visibility, true
445 p.add("statusbar_visibility", yes_no(
446 statusbar_visibility, true
448 p.add("titles_visibility", yes_no(
449 titles_visibility, true
451 p.add("header_text_scrolling", yes_no(
452 header_text_scrolling, true
454 p.add("cyclic_scrolling", yes_no(
455 use_cyclic_scrolling, false
457 p.add("lines_scrolled", assign_default(
458 lines_scrolled, 2
460 p.add("follow_now_playing_lyrics", yes_no(
461 now_playing_lyrics, false
463 p.add("fetch_lyrics_for_current_song_in_background", yes_no(
464 fetch_lyrics_in_background, false
466 p.add("store_lyrics_in_song_dir", yes_no(
467 store_lyrics_in_song_dir, false
469 p.add("generate_win32_compatible_filenames", yes_no(
470 generate_win32_compatible_filenames, true
472 p.add("allow_for_physical_item_deletion", yes_no(
473 allow_for_physical_item_deletion, false
475 p.add("lastfm_preferred_language", assign_default(
476 lastfm_preferred_language, "en"
478 p.add("space_add_mode", assign_default(
479 space_add_mode, SpaceAddMode::AlwaysAdd
481 p.add("show_hidden_files_in_local_browser", yes_no(
482 local_browser_show_hidden_files, false
484 p.add("screen_switcher_mode", option_parser::worker([this](std::string &&v) {
485 if (v == "previous")
486 screen_switcher_previous = true;
487 else
489 screen_switcher_previous = false;
490 boost::sregex_token_iterator i(v.begin(), v.end(), boost::regex("\\w+")), j;
491 for (; i != j; ++i)
493 auto screen = stringtoStartupScreenType(*i);
494 if (screen != ScreenType::Unknown)
495 screen_sequence.push_back(screen);
496 else
497 throw std::runtime_error("unknown screen: " + *i);
500 }, [this] {
501 screen_switcher_previous = false;
502 screen_sequence = { ScreenType::Playlist, ScreenType::Browser };
503 }));
504 p.add("startup_screen", option_parser::worker([this](std::string &&v) {
505 startup_screen_type = stringtoStartupScreenType(v);
506 if (startup_screen_type == ScreenType::Unknown)
507 throw std::runtime_error("unknown screen: " + v);
508 }, defaults_to(startup_screen_type, ScreenType::Playlist)
510 p.add("locked_screen_width_part", assign_default<double>(
511 locked_screen_width_part, 50.0, [](double v) {
512 return v / 100;
513 }));
514 p.add("ask_for_locked_screen_width_part", yes_no(
515 ask_for_locked_screen_width_part, true
517 p.add("jump_to_now_playing_song_at_start", yes_no(
518 jump_to_now_playing_song_at_start, true
520 p.add("ask_before_clearing_playlists", yes_no(
521 ask_before_clearing_playlists, true
523 p.add("clock_display_seconds", yes_no(
524 clock_display_seconds, false
526 p.add("display_volume_level", yes_no(
527 display_volume_level, true
529 p.add("display_bitrate", yes_no(
530 display_bitrate, false
532 p.add("display_remaining_time", yes_no(
533 display_remaining_time, false
535 p.add("regular_expressions", option_parser::worker([this](std::string &&v) {
536 if (v == "none")
537 regex_type = boost::regex::literal;
538 else if (v == "basic")
539 regex_type = boost::regex::basic;
540 else if (v == "extended")
541 regex_type = boost::regex::extended;
542 else
543 throw std::runtime_error("invalid argument: " + v);
544 regex_type |= boost::regex::icase;
545 }, defaults_to(regex_type, boost::regex::literal | boost::regex::icase)
547 p.add("ignore_leading_the", yes_no(
548 ignore_leading_the, false
550 p.add("block_search_constraints_change_if_items_found", yes_no(
551 block_search_constraints_change, true
553 p.add("mouse_support", yes_no(
554 mouse_support, true
556 p.add("mouse_list_scroll_whole_page", yes_no(
557 mouse_list_scroll_whole_page, true
559 p.add("empty_tag_marker", assign_default(
560 empty_tag, "<empty>"
562 p.add("tags_separator", assign_default(
563 tags_separator, " | "
565 p.add("tag_editor_extended_numeration", yes_no(
566 tag_editor_extended_numeration, false
568 p.add("media_library_sort_by_mtime", yes_no(
569 media_library_sort_by_mtime, false
571 p.add("enable_window_title", yes_no(
572 set_window_title, true
574 p.add("search_engine_default_search_mode", assign_default<unsigned>(
575 search_engine_default_search_mode, 1, [](unsigned v) {
576 boundsCheck(v, 1u, 3u);
577 return --v;
578 }));
579 p.add("external_editor", assign_default(
580 external_editor, "nano"
582 p.add("use_console_editor", yes_no(
583 use_console_editor, true
585 p.add("colors_enabled", yes_no(
586 colors_enabled, true
588 p.add("empty_tag_color", assign_default(
589 empty_tags_color, NC::Color::Cyan
591 p.add("header_window_color", assign_default(
592 header_color, NC::Color::Default
594 p.add("volume_color", assign_default(
595 volume_color, NC::Color::Default
597 p.add("state_line_color", assign_default(
598 state_line_color, NC::Color::Default
600 p.add("state_flags_color", assign_default(
601 state_flags_color, NC::Color::Default
603 p.add("main_window_color", assign_default(
604 main_color, NC::Color::Yellow
606 p.add("color1", assign_default(
607 color1, NC::Color::White
609 p.add("color2", assign_default(
610 color2, NC::Color::Green
612 p.add("main_window_highlight_color", assign_default(
613 main_highlight_color, NC::Color::Yellow
615 p.add("progressbar_color", assign_default(
616 progressbar_color, NC::Color::Black
618 p.add("progressbar_elapsed_color", assign_default(
619 progressbar_elapsed_color, NC::Color::Green
621 p.add("statusbar_color", assign_default(
622 statusbar_color, NC::Color::Default
624 p.add("alternative_ui_separator_color", assign_default(
625 alternative_ui_separator_color, NC::Color::Black
627 p.add("active_column_color", assign_default(
628 active_column_color, NC::Color::Red
630 p.add("visualizer_color", assign_default(
631 visualizer_color, NC::Color::Yellow
633 p.add("window_border_color", assign_default(
634 window_border, NC::Border::Green
636 p.add("active_window_border", assign_default(
637 active_window_border, NC::Border::Red
640 std::ifstream f(config_path);
641 return p.run(f);