diff options
author | Andrzej Rybczak <electricityispower@gmail.com> | 2014-11-10 00:31:53 +0100 |
---|---|---|
committer | Andrzej Rybczak <electricityispower@gmail.com> | 2014-11-11 00:08:23 +0100 |
commit | 8d24c42261c17d18ce723df5a65cc9d1e8dc3a9d (patch) | |
tree | 748e5215b607611a2d8c39625f5645e21bfa5d2c /src | |
parent | 25708093e6098a510b79dcbc715fa2fa465bd859 (diff) |
format: implement generic format parser and printer
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/actions.cpp | 2 | ||||
-rw-r--r-- | src/browser.cpp | 4 | ||||
-rw-r--r-- | src/display.cpp | 67 | ||||
-rw-r--r-- | src/display.h | 3 | ||||
-rw-r--r-- | src/format.cpp | 209 | ||||
-rw-r--r-- | src/format.h | 279 | ||||
-rw-r--r-- | src/helpers.cpp | 2 | ||||
-rw-r--r-- | src/helpers.h | 89 | ||||
-rw-r--r-- | src/lyrics.cpp | 9 | ||||
-rw-r--r-- | src/media_library.cpp | 12 | ||||
-rw-r--r-- | src/mutable_song.cpp | 4 | ||||
-rw-r--r-- | src/mutable_song.h | 2 | ||||
-rw-r--r-- | src/playlist.cpp | 12 | ||||
-rw-r--r-- | src/playlist_editor.cpp | 14 | ||||
-rw-r--r-- | src/search_engine.cpp | 4 | ||||
-rw-r--r-- | src/settings.cpp | 103 | ||||
-rw-r--r-- | src/settings.h | 24 | ||||
-rw-r--r-- | src/song.cpp | 137 | ||||
-rw-r--r-- | src/song.h | 13 | ||||
-rw-r--r-- | src/song_info.cpp | 2 | ||||
-rw-r--r-- | src/sort_playlist.cpp | 4 | ||||
-rw-r--r-- | src/status.cpp | 8 | ||||
-rw-r--r-- | src/tag_editor.cpp | 14 | ||||
-rw-r--r-- | src/tags.cpp | 2 | ||||
-rw-r--r-- | src/tiny_tag_editor.cpp | 6 | ||||
-rw-r--r-- | src/utility/comparators.cpp | 4 | ||||
-rw-r--r-- | src/utility/type_conversions.cpp | 2 | ||||
-rw-r--r-- | src/utility/wide_string.cpp | 2 |
29 files changed, 636 insertions, 399 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index f7f70c5c..bd4579d2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,6 +16,7 @@ ncmpcpp_SOURCES = \ display.cpp \ enums.cpp \ error.cpp \ + format.cpp \ global.cpp \ help.cpp \ helpers.cpp \ @@ -73,6 +74,7 @@ noinst_HEADERS = \ display.h \ enums.h \ error.h \ + format.h \ global.h \ help.h \ helpers.h \ diff --git a/src/actions.cpp b/src/actions.cpp index 1b5a0fa5..7f9bf662 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -1322,7 +1322,7 @@ void EditLibraryTag::run() for (MPD::SongIterator s = Mpd.CommitSearchSongs(), end; s != end; ++s) { MPD::MutableSong ms = std::move(*s); - ms.setTags(set, new_tag, Config.tags_separator); + ms.setTags(set, new_tag); Statusbar::printf("Updating tags in \"%1%\"...", ms.getName()); std::string path = Config.mpd_music_dir + ms.getURI(); if (!Tags::write(ms)) diff --git a/src/browser.cpp b/src/browser.cpp index a009a8a2..a0afe361 100644 --- a/src/browser.cpp +++ b/src/browser.cpp @@ -641,10 +641,10 @@ std::string itemToString(const MPD::Item &item) switch (Config.browser_display_mode) { case DisplayMode::Classic: - result = item.song().toString(Config.song_list_format_dollar_free, Config.tags_separator); + result = Format::stringify<char>(Config.song_list_format, &item.song()); break; case DisplayMode::Columns: - result = item.song().toString(Config.song_in_columns_to_string_format, Config.tags_separator); + result = Format::stringify<char>(Config.song_columns_mode_format, &item.song()); break; } break; diff --git a/src/display.cpp b/src/display.cpp index ac31c720..dd05827e 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -104,57 +104,25 @@ void setProperties(NC::Menu<T> &menu, const MPD::Song &s, const ProxySongList &p } template <typename T> -void showSongs(NC::Menu<T> &menu, const MPD::Song &s, const ProxySongList &pl, const std::string &format) +void showSongs(NC::Menu<T> &menu, const MPD::Song &s, + const ProxySongList &pl, const Format::AST<char> &ast) { bool separate_albums, is_now_playing, is_selected, discard_colors; setProperties(menu, s, pl, separate_albums, is_now_playing, is_selected, discard_colors); - + size_t y = menu.getY(); - std::string line = Charset::utf8ToLocale(s.toString(format, Config.tags_separator, "$")); - for (auto it = line.begin(); it != line.end(); ++it) + NC::Buffer right_aligned; + Format::print(ast, menu, &s, &right_aligned); + if (!right_aligned.str().empty()) { - if (*it == '$') - { - ++it; - if (it == line.end()) // end of format - { - menu << '$'; - break; - } - else if (isdigit(*it)) // color - { - if (!discard_colors) - menu << charToColor(*it); - } - else if (*it == 'R') // right align - { - NC::Buffer buf; - buf << " "; - stringToBuffer(++it, line.end(), buf); - if (discard_colors) - buf.removeProperties(); - size_t x_off = menu.getWidth() - wideLength(ToWString(buf.str())); - if (is_now_playing) - x_off -= Config.now_playing_suffix_length; - if (is_selected) - x_off -= Config.selected_item_suffix_length; - menu << NC::XY(x_off, y) << buf; - break; - } - else // not a color nor right align, just a random character - menu << *--it; - } - else if (*it == MPD::Song::FormatEscapeCharacter) - { - ++it; - // treat '$' as a normal character if song format escape char is prepended to it - if (it == line.end() || *it != '$') - --it; - menu << *it; - } - else - menu << *it; + size_t x_off = menu.getWidth() - wideLength(ToWString(right_aligned.str())); + if (is_now_playing) + x_off -= Config.now_playing_suffix_length; + if (is_selected) + x_off -= Config.selected_item_suffix_length; + menu << NC::XY(x_off, y) << right_aligned; } + if (is_now_playing) menu << Config.now_playing_suffix; if (separate_albums) @@ -225,7 +193,7 @@ void showSongsInColumns(NC::Menu<T> &menu, const MPD::Song &s, const ProxySongLi { MPD::Song::GetFunction get = charToGetFunction(it->type[i]); assert(get); - tag = ToWString(Charset::utf8ToLocale(s.getTags(get, Config.tags_separator))); + tag = ToWString(Charset::utf8ToLocale(s.getTags(get))); if (!tag.empty()) break; } @@ -353,9 +321,10 @@ void Display::SongsInColumns(NC::Menu< MPD::Song >& menu, const ProxySongList &p showSongsInColumns(menu, menu.drawn()->value(), pl); } -void Display::Songs(NC::Menu< MPD::Song >& menu, const ProxySongList &pl, const std::string &format) +void Display::Songs(NC::Menu< MPD::Song >& menu, + const ProxySongList &pl, const Format::AST<char> &ast) { - showSongs(menu, menu.drawn()->value(), pl, format); + showSongs(menu, menu.drawn()->value(), pl, ast); } #ifdef HAVE_TAGLIB_H @@ -367,7 +336,7 @@ void Display::Tags(NC::Menu<MPD::MutableSong> &menu) size_t i = myTagEditor->TagTypes->choice(); if (i < 11) { - ShowTag(menu, Charset::utf8ToLocale(s.getTags(SongInfo::Tags[i].Get, Config.tags_separator))); + ShowTag(menu, Charset::utf8ToLocale(s.getTags(SongInfo::Tags[i].Get))); } else if (i == 12) { diff --git a/src/display.h b/src/display.h index 18817f19..55a9b586 100644 --- a/src/display.h +++ b/src/display.h @@ -22,6 +22,7 @@ #define NCMPCPP_DISPLAY_H #include "interfaces.h" +#include "format.h" #include "menu.h" #include "mutable_song.h" #include "search_engine.h" @@ -32,7 +33,7 @@ std::string Columns(size_t); void SongsInColumns(NC::Menu<MPD::Song> &menu, const ProxySongList &pl); -void Songs(NC::Menu<MPD::Song> &menu, const ProxySongList &pl, const std::string &format); +void Songs(NC::Menu<MPD::Song> &menu, const ProxySongList &pl, const Format::AST<char> &ast); void Tags(NC::Menu<MPD::MutableSong> &menu); diff --git a/src/format.cpp b/src/format.cpp new file mode 100644 index 00000000..20fa6d7a --- /dev/null +++ b/src/format.cpp @@ -0,0 +1,209 @@ +/*************************************************************************** + * 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 <stdexcept> + +#include "format.h" +#include "utility/type_conversions.h" + +namespace { + +template <typename CharT> using string = std::basic_string<CharT>; +template <typename CharT> using iterator = typename std::basic_string<CharT>::const_iterator; +template <typename CharT> using expressions = std::vector<Format::Expression<CharT>>; + +std::string invalidCharacter(char c) +{ + return "invalid character '" + boost::lexical_cast<std::string>(c) + "'"; +} + +template <typename CharT> +void throwError(const string<CharT> &s, iterator<CharT> current, std::string msg) +{ + throw std::runtime_error( + std::move(msg) + " at position " + boost::lexical_cast<std::string>(current - s.begin()) + ); +} + +template <typename CharT> +void rangeCheck(const string<CharT> &s, iterator<CharT> current, iterator<CharT> end) +{ + if (current >= end) + throwError(s, current, "unexpected end"); +} + +template <typename CharT> +expressions<CharT> parseBracket(const string<CharT> &s, + iterator<CharT> it, iterator<CharT> end) +{ + string<CharT> token; + expressions<CharT> tmp, result; + auto push_token = [&] { + result.push_back(std::move(token)); + }; + for (; it != end; ++it) + { + if (*it == '{') + { + push_token(); + bool done; + Format::Any<CharT> any; + do + { + auto jt = it; + done = true; + // get to the corresponding closing bracket + unsigned brackets = 1; + while (brackets > 0) + { + if (++jt == end) + break; + if (*jt == '{') + ++brackets; + else if (*jt == '}') + --brackets; + } + // check if we're still in range + rangeCheck(s, jt, end); + // skip the opening bracket + ++it; + // recursively parse the bracket + tmp = parseBracket(s, it, jt); + // if the inner bracket contains only one expression, + // put it as is. otherwise require all of them. + if (tmp.size() == 1) + any.base().push_back(std::move(tmp[0])); + else + any.base().push_back(Format::All<CharT>(std::move(tmp))); + it = jt; + // check for the alternative + ++jt; + if (jt != end && *jt == '|') + { + ++jt; + rangeCheck(s, jt, end); + if (*jt != '{') + throwError(s, jt, invalidCharacter(*jt) + ", expected '{'"); + it = jt; + done = false; + } + } + while (!done); + assert(!any.base().empty()); + // if there was only one bracket, append empty branch + // so that it always evaluates to true. otherwise put + // it as is. + if (any.base().size() == 1) + any.base().push_back(string<CharT>()); + result.push_back(std::move(any)); + } + else if (*it == '%') + { + ++it; + rangeCheck(s, it, end); + // %% is escaped % + if (*it == '%') + { + token += '%'; + continue; + } + push_token(); + // check for tag delimiter + unsigned delimiter = 0; + if (isdigit(*it)) + { + std::string sdelimiter; + do + sdelimiter += *it++; + while (it != end && isdigit(*it)); + rangeCheck(s, it, end); + delimiter = boost::lexical_cast<unsigned>(sdelimiter); + } + auto f = charToGetFunction(*it); + if (f == nullptr) + throwError(s, it, invalidCharacter(*it)); + result.push_back(Format::SongTag(f, delimiter)); + } + else if (*it == '$') + { + ++it; + rangeCheck(s, it, end); + // $$ is escaped $ + if (*it == '$') + { + token += '$'; + continue; + } + push_token(); + if (isdigit(*it)) + { + auto color = charToColor(*it); + result.push_back(color); + } + else if (*it == 'R') + result.push_back(Format::AlignRight()); + else if (*it == 'b') + result.push_back(NC::Format::Bold); + else if (*it == 'u') + result.push_back(NC::Format::Underline); + else if (*it == 'a') + result.push_back(NC::Format::AltCharset); + else if (*it == 'r') + result.push_back(NC::Format::Reverse); + else if (*it == '/') + { + ++it; + rangeCheck(s, it, end); + if (*it == 'b') + result.push_back(NC::Format::NoBold); + else if (*it == 'u') + result.push_back(NC::Format::NoUnderline); + else if (*it == 'a') + result.push_back(NC::Format::NoAltCharset); + else if (*it == 'r') + result.push_back(NC::Format::NoReverse); + else + throwError(s, it, invalidCharacter(*it)); + } + else + throwError(s, it, invalidCharacter(*it)); + } + else + token += *it; + } + push_token(); + return result; +} + +} + +namespace Format { + +AST<char> parse(const std::string &s) +{ + return AST<char>(parseBracket(s, s.begin(), s.end())); +} + +AST<wchar_t> wparse(const std::wstring &s) +{ + return AST<wchar_t>(parseBracket(s, s.begin(), s.end())); +} + +} diff --git a/src/format.h b/src/format.h new file mode 100644 index 00000000..282c7257 --- /dev/null +++ b/src/format.h @@ -0,0 +1,279 @@ +/*************************************************************************** + * 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_HAVE_FORMAT_H +#define NCMPCPP_HAVE_FORMAT_H + +#include <boost/locale/encoding_utf.hpp> +#include <boost/variant.hpp> + +#include "menu.h" +#include "song.h" +#include "strbuffer.h" +#include "utility/wide_string.h" + +namespace Format { + +enum class ListType { All, Any, AST }; + +template <ListType, typename> struct List; +template <typename CharT> using All = List<ListType::All, CharT>; +template <typename CharT> using Any = List<ListType::Any, CharT>; +template <typename CharT> using AST = List<ListType::AST, CharT>; + +struct AlignRight { }; + +struct SongTag +{ + SongTag(MPD::Song::GetFunction function_, unsigned delimiter_ = 0) + : m_function(function_), m_delimiter(delimiter_) + { } + + MPD::Song::GetFunction function() const { return m_function; } + unsigned delimiter() const { return m_delimiter; } + +private: + MPD::Song::GetFunction m_function; + unsigned m_delimiter; +}; + +template <typename CharT> +using Expression = boost::variant< + std::basic_string<CharT>, + NC::Color, + NC::Format, + AlignRight, + SongTag, + boost::recursive_wrapper<Any<CharT>>, + boost::recursive_wrapper<All<CharT>> +>; + +template <ListType Type, typename CharT> +struct List +{ + typedef std::vector<Expression<CharT>> Base; + + List() { } + List(Base &&base_) + : m_base(std::move(base_)) + { } + + Base &base() { return m_base; } + const Base &base() const { return m_base; } + +private: + Base m_base; +}; + +template <typename CharT, typename OutputT, typename OutputAfterAlignmentT = OutputT> +struct Printer: boost::static_visitor<bool> +{ + typedef std::basic_string<CharT> StringT; + + Printer(OutputT &os, const MPD::Song *song, OutputAfterAlignmentT *os_after_alignment) + : m_output(os) + , m_song(song) + , m_after_alignment(false) + , m_output_after_alignment(os_after_alignment) + , m_no_output(0) + { } + + bool operator()(const StringT &s) + { + output(s); + return true; + } + + bool operator()(const NC::Color &c) + { + output(c); + return true; + } + + bool operator()(NC::Format fmt) + { + output(fmt); + return true; + } + + bool operator()(AlignRight) + { + if (!m_no_output) + m_after_alignment = true; + return true; + } + + bool operator()(const SongTag &st) + { + StringT tags; + if (m_song != nullptr) + { + tags = convertString<std::is_same<char, CharT>::value, std::string>::apply( + m_song->getTags(st.function()) + ); + } + if (!tags.empty()) + { + if (st.delimiter() > 0) + { + // shorten length by chopping off the tail + if (st.function() == &MPD::Song::getLength) + tags.resize(st.delimiter()); + else + tags = wideShorten(tags, st.delimiter()); + } + output(tags); + return true; + } + else + return false; + } + + bool operator()(const All<CharT> &all) + { + auto visit = [this, &all] { + return std::all_of( + all.base().begin(), + all.base().end(), + [this](const Expression<CharT> &ex) { + return boost::apply_visitor(*this, ex); + } + ); + }; + ++m_no_output; + bool all_ok = visit(); + --m_no_output; + if (!m_no_output && all_ok) + visit(); + return all_ok; + } + + bool operator()(const Any<CharT> &any) + { + return std::any_of( + any.base().begin(), + any.base().end(), + [this](const Expression<CharT> &ex) { + return boost::apply_visitor(*this, ex); + } + ); + } + +private: + // convert string to appropriate type + template <bool AreSame, typename ValueT> + struct convertString { + static const StringT &apply(const ValueT &s) { + return s; + } + }; + template <typename ValueT> + struct convertString<false, ValueT> { + static StringT apply(const ValueT &s) { + return boost::locale::conv::utf_to_utf<CharT>(s); + } + }; + + // generic version for streams (buffers, menus) + template <typename ValueT, typename OutputStreamT> + struct output_ { + static void exec(OutputStreamT &os, const ValueT &value) { + os << value; + } + }; + // specialization for strings (input/output) + template <typename SomeCharT, typename OtherCharT> + struct output_<std::basic_string<SomeCharT>, std::basic_string<OtherCharT>> { + typedef std::basic_string<SomeCharT> SomeString; + typedef std::basic_string<OtherCharT> OtherString; + + // compile only if string types are the same + static typename std::enable_if< + std::is_same<SomeString, OtherString>::value, + void + >::type exec(SomeString &result, const OtherString &s) { + result += s; + } + }; + // when writing to a string, ignore all properties + template <typename ValueT, typename SomeCharT> + struct output_<ValueT, std::basic_string<SomeCharT>> + { + static void exec(std::basic_string<CharT> &, const ValueT &) { } + }; + + template <typename ValueT> + void output(const ValueT &value) const + { + if (!m_no_output) + { + if (m_after_alignment && m_output_after_alignment != nullptr) + output_<ValueT, OutputAfterAlignmentT>::exec(*m_output_after_alignment, value); + else + output_<ValueT, OutputT>::exec(m_output, value); + } + } + + OutputT &m_output; + const MPD::Song *m_song; + + bool m_after_alignment; + OutputAfterAlignmentT *m_output_after_alignment; + + unsigned m_no_output; +}; + +template <typename CharT, typename VisitorT> +void visit(VisitorT &visitor, const AST<CharT> &ast) +{ + for (const auto &ex : ast.base()) + boost::apply_visitor(visitor, ex); +} + +template <typename CharT, typename ItemT> +void print(const AST<CharT> &ast, NC::Menu<ItemT> &menu, + const MPD::Song *song, NC::BasicBuffer<CharT> *buffer) +{ + Printer<CharT, NC::Menu<ItemT>, NC::Buffer> printer(menu, song, buffer); + visit(printer, ast); +} + +template <typename CharT> +void print(const AST<CharT> &ast, NC::BasicBuffer<CharT> &buffer, const MPD::Song *song) +{ + Printer<CharT, NC::BasicBuffer<CharT>> printer(buffer, song, &buffer); + visit(printer, ast); +} + +template <typename CharT> +std::basic_string<CharT> stringify(const AST<CharT> &ast, const MPD::Song *song) +{ + std::basic_string<CharT> result; + Printer<CharT, std::basic_string<CharT>> printer(result, song, &result); + visit(printer, ast); + return result; +} + +AST<char> parse(const std::string &s); +AST<wchar_t> wparse(const std::wstring &ws); + +} + +#endif // NCMPCPP_HAVE_FORMAT_H diff --git a/src/helpers.cpp b/src/helpers.cpp index f9832376..a70a9fcd 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -54,7 +54,7 @@ bool addSongToPlaylist(const MPD::Song &s, bool play, int position) if (id >= 0) { Statusbar::printf("Added to playlist: %s", - s.toString(Config.song_status_format_no_colors, Config.tags_separator) + Format::stringify<char>(Config.song_status_format, &s) ); if (play) Mpd.PlayID(id); diff --git a/src/helpers.h b/src/helpers.h index 945facb4..1861d471 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -331,95 +331,6 @@ template <typename Iterator> std::string getSharedDirectory(Iterator first, Iter return result; } -template <typename Iterator> -void stringToBuffer(Iterator first, Iterator last, NC::BasicBuffer<typename Iterator::value_type> &buf) -{ - for (auto it = first; it != last; ++it) - { - if (*it == '$') - { - if (++it == last) - { - buf << '$'; - break; - } - else if (isdigit(*it)) - { - buf << charToColor(*it); - } - else - { - switch (*it) - { - case 'b': - buf << NC::Format::Bold; - break; - case 'u': - buf << NC::Format::Underline; - break; - case 'a': - buf << NC::Format::AltCharset; - break; - case 'r': - buf << NC::Format::Reverse; - break; - case '/': - if (++it == last) - { - buf << '$' << '/'; - break; - } - switch (*it) - { - case 'b': - buf << NC::Format::NoBold; - break; - case 'u': - buf << NC::Format::NoUnderline; - break; - case 'a': - buf << NC::Format::NoAltCharset; - break; - case 'r': - buf << NC::Format::NoReverse; - break; - default: - buf << '$' << *--it; - break; - } - break; - default: - buf << *--it; - break; - } - } - } - else if (*it == MPD::Song::FormatEscapeCharacter) - { - // treat '$' as a normal character if song format escape char is prepended to it - if (++it == last || *it != '$') - --it; - buf << *it; - } - else - buf << *it; - } -} - -template <typename CharT> -void stringToBuffer(const std::basic_string<CharT> &s, NC::BasicBuffer<CharT> &buf) -{ - stringToBuffer(s.begin(), s.end(), buf); -} - -template <typename CharT> -NC::BasicBuffer<CharT> stringToBuffer(const std::basic_string<CharT> &s) -{ - NC::BasicBuffer<CharT> result; - stringToBuffer(s, result); - return result; -} - template <typename T> void ShowTime(T &buf, size_t length, bool short_names) { const unsigned MINUTE = 60; diff --git a/src/lyrics.cpp b/src/lyrics.cpp index 000c3c45..2d0450ff 100644 --- a/src/lyrics.cpp +++ b/src/lyrics.cpp @@ -127,7 +127,12 @@ void Lyrics::switchTo() std::wstring Lyrics::title() { std::wstring result = L"Lyrics: "; - result += Scroller(ToWString(itsSong.toString("{%a - %t}", ", ")), itsScrollBegin, COLS-result.length()-(Config.design == Design::Alternative ? 2 : Global::VolumeState.length())); + + result += Scroller( + Format::stringify<wchar_t>(Format::wparse(L"{%a - %t}"), &itsSong), + itsScrollBegin, + COLS-result.length()-(Config.design == Design::Alternative ? 2 : Global::VolumeState.length()) + ); return result; } @@ -153,7 +158,7 @@ void Lyrics::DownloadInBackground(const MPD::Song &s) return; } Statusbar::printf("Fetching lyrics for \"%1%\"...", - s.toString(Config.song_status_format_no_colors, Config.tags_separator) + Format::stringify<char>(Config.song_status_format, &s) ); MPD::Song *s_copy = new MPD::Song(s); diff --git a/src/media_library.cpp b/src/media_library.cpp index baebbd62..a7680d26 100644 --- a/src/media_library.cpp +++ b/src/media_library.cpp @@ -93,8 +93,8 @@ public: } bool operator()(const MPD::Song &a, const MPD::Song &b) { for (auto get = GetFuns.begin()+m_offset; get != GetFuns.end(); ++get) { - int ret = m_cmp(a.getTags(*get, Config.tags_separator), - b.getTags(*get, Config.tags_separator)); + int ret = m_cmp(a.getTags(*get), + b.getTags(*get)); if (ret != 0) return ret < 0; } @@ -197,7 +197,9 @@ MediaLibrary::MediaLibrary() Songs.centeredCursor(Config.centered_cursor); Songs.setSelectedPrefix(Config.selected_item_prefix); Songs.setSelectedSuffix(Config.selected_item_suffix); - Songs.setItemDisplayer(boost::bind(Display::Songs, _1, songsProxyList(), Config.song_library_format)); + Songs.setItemDisplayer(boost::bind( + Display::Songs, _1, songsProxyList(), Config.song_library_format + )); w = &Tags; } @@ -1037,7 +1039,9 @@ std::string AlbumToString(const AlbumEntry &ae) std::string SongToString(const MPD::Song &s) { - return s.toString(Config.song_library_format, Config.tags_separator); + return Format::stringify<char>( + Config.song_library_format, &s//FIXME, Config.tags_separator + ); } bool TagEntryMatcher(const boost::regex &rx, const MediaLibrary::PrimaryTag &pt) diff --git a/src/mutable_song.cpp b/src/mutable_song.cpp index 6b99e886..ad4dadc3 100644 --- a/src/mutable_song.cpp +++ b/src/mutable_song.cpp @@ -178,10 +178,10 @@ void MutableSong::setMTime(time_t mtime) m_mtime = mtime; } -void MutableSong::setTags(SetFunction set, const std::string &value, const std::string &delimiter) +void MutableSong::setTags(SetFunction set, const std::string &value) { std::vector<std::string> tags; - boost::iter_split(tags, value, boost::first_finder(delimiter)); + boost::iter_split(tags, value, boost::first_finder(Song::TagsSeparator)); size_t i = 0; for (; i < tags.size(); ++i) (this->*set)(tags[i], i); diff --git a/src/mutable_song.h b/src/mutable_song.h index 96ffdfbb..f10b80e8 100644 --- a/src/mutable_song.h +++ b/src/mutable_song.h @@ -66,7 +66,7 @@ struct MutableSong : public Song void setDuration(unsigned duration); void setMTime(time_t mtime); - void setTags(SetFunction set, const std::string &value, const std::string &delimiter); + void setTags(SetFunction set, const std::string &value); bool isModified() const; void clearModifications(); diff --git a/src/playlist.cpp b/src/playlist.cpp index bdb99ea4..ef7dbd54 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -61,10 +61,14 @@ Playlist::Playlist() switch (Config.playlist_display_mode) { case DisplayMode::Classic: - w.setItemDisplayer(boost::bind(Display::Songs, _1, proxySongList(), Config.song_list_format)); + w.setItemDisplayer(boost::bind( + Display::Songs, _1, proxySongList(), Config.song_list_format + )); break; case DisplayMode::Columns: - w.setItemDisplayer(boost::bind(Display::SongsInColumns, _1, proxySongList())); + w.setItemDisplayer(boost::bind( + Display::SongsInColumns, _1, proxySongList() + )); break; } } @@ -307,10 +311,10 @@ std::string songToString(const MPD::Song &s) switch (Config.playlist_display_mode) { case DisplayMode::Classic: - result = s.toString(Config.song_list_format_dollar_free, Config.tags_separator); + result = Format::stringify<char>(Config.song_list_format, &s); break; case DisplayMode::Columns: - result = s.toString(Config.song_in_columns_to_string_format, Config.tags_separator); + result = Format::stringify<char>(Config.song_columns_mode_format, &s); } return result; } diff --git a/src/playlist_editor.cpp b/src/playlist_editor.cpp index 11aaf097..180543a3 100644 --- a/src/playlist_editor.cpp +++ b/src/playlist_editor.cpp @@ -83,10 +83,14 @@ PlaylistEditor::PlaylistEditor() switch (Config.playlist_editor_display_mode) { case DisplayMode::Classic: - Content.setItemDisplayer(boost::bind(Display::Songs, _1, contentProxyList(), Config.song_list_format)); + Content.setItemDisplayer( + boost::bind(Display::Songs, _1, contentProxyList(), Config.song_list_format + )); break; case DisplayMode::Columns: - Content.setItemDisplayer(boost::bind(Display::SongsInColumns, _1, contentProxyList())); + Content.setItemDisplayer( + boost::bind(Display::SongsInColumns, _1, contentProxyList()) + ); break; } @@ -491,7 +495,7 @@ void PlaylistEditor::Locate(const MPD::Playlist &playlist) } } -namespace {// +namespace { std::string SongToString(const MPD::Song &s) { @@ -499,10 +503,10 @@ std::string SongToString(const MPD::Song &s) switch (Config.playlist_display_mode) { case DisplayMode::Classic: - result = s.toString(Config.song_list_format_dollar_free, Config.tags_separator); + result = Format::stringify<char>(Config.song_list_format, &s); break; case DisplayMode::Columns: - result = s.toString(Config.song_in_columns_to_string_format, Config.tags_separator); + result = Format::stringify<char>(Config.song_columns_mode_format, &s); break; } return result; diff --git a/src/search_engine.cpp b/src/search_engine.cpp index 1286e4bc..bc96e9f6 100644 --- a/src/search_engine.cpp +++ b/src/search_engine.cpp @@ -591,10 +591,10 @@ std::string SEItemToString(const SEItem &ei) switch (Config.search_engine_display_mode) { case DisplayMode::Classic: - result = ei.song().toString(Config.song_list_format_dollar_free, Config.tags_separator); + result = Format::stringify<char>(Config.song_list_format, &ei.song()); break; case DisplayMode::Columns: - result = ei.song().toString(Config.song_in_columns_to_string_format, Config.tags_separator); + result = Format::stringify<char>(Config.song_columns_mode_format, &ei.song()); break; } } diff --git a/src/settings.cpp b/src/settings.cpp index 4dd82ac8..b97fccc7 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -37,20 +37,7 @@ Configuration Config; namespace { -std::string remove_dollar_formatting(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; -} - -std::pair<std::vector<Column>, std::string> generate_columns(const std::string &format) +std::vector<Column> generate_columns(const std::string &format) { std::vector<Column> result; std::string width; @@ -128,25 +115,32 @@ std::pair<std::vector<Column>, std::string> generate_columns(const std::string & it->stretch_limit = stretch_limit; } - std::string string_format; - // generate format for converting tags in columns to string for Playlist::SongInColumnsToString() - char tag[] = "{% }|"; - string_format = "{"; - for (auto it = result.begin(); it != result.end(); ++it) + return result; +} + +Format::AST<char> columns_to_format(const std::vector<Column> &columns) +{ + std::vector<Format::Expression<char>> result; + + auto column = columns.begin(); + while (true) { - for (std::string::const_iterator j = it->type.begin(); j != it->type.end(); ++j) + Format::Any<char> any; + for (const auto &type : column->type) { - tag[2] = *j; - string_format += tag; + auto f = charToGetFunction(type); + assert(f != nullptr); + any.base().push_back(f); } - *string_format.rbegin() = ' '; + result.push_back(std::move(any)); + + if (++column != columns.end()) + result.push_back(" "); + else + break; } - if (string_format.length() == 1) // only '{' - string_format += '}'; - else - *string_format.rbegin() = '}'; - return std::make_pair(std::move(result), std::move(string_format)); + return Format::AST<char>(std::move(result)); } void add_slash_at_the_end(std::string &s) @@ -165,19 +159,14 @@ std::string adjust_directory(std::string s) return s; } -std::string adjust_and_validate_format(std::string format) -{ - MPD::Song::validateFormat(format); - format = "{" + format + "}"; - return format; -} - // parser worker for buffer template <typename ValueT, typename TransformT> option_parser::worker buffer(NC::Buffer &arg, ValueT &&value, TransformT &&map) { return option_parser::worker(assign<std::string>(arg, [&arg, map](std::string s) { - return map(stringToBuffer(std::move(s))); + NC::Buffer result; + Format::print(Format::parse(s), result, nullptr); + return result; }), defaults_to(arg, map(std::forward<ValueT>(value)))); } @@ -282,38 +271,31 @@ bool Configuration::read(const std::string &config_path) message_delay_time, 5 )); p.add("song_list_format", assign_default<std::string>( - 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; - })); + song_list_format, "{%a - }{%t}|{$8%f$9}$R{$3(%l)$9}", Format::parse + )); p.add("song_status_format", assign_default<std::string>( - 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; + song_status_format, "{{%a{ \"%b\"{ (%y)}} - }{%t}}|{%f}", [this](std::string v) { + // precompute wide format for status display + song_status_wformat = Format::wparse(ToWString(v)); + return Format::parse(v); })); p.add("song_library_format", assign_default<std::string>( - song_library_format, "{%n - }{%t}|{%f}", adjust_and_validate_format - )); - p.add("tag_editor_album_format", assign_default<std::string>( - tag_editor_album_format, "{(%y) }%b", adjust_and_validate_format + song_library_format, "{%n - }{%t}|{%f}", Format::parse )); p.add("browser_sort_mode", assign_default( browser_sort_mode, SortMode::Name )); p.add("browser_sort_format", assign_default<std::string>( - browser_sort_format, "{%a - }{%t}|{%f} {(%l)}", adjust_and_validate_format + browser_sort_format, "{%a - }{%t}|{%f} {(%l)}", Format::parse )); p.add("alternative_header_first_line_format", assign_default<std::string>( - new_header_first_line, "$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b", adjust_and_validate_format - )); + new_header_first_line, "$b$1$aqqu$/a$9 {%t}|{%f} $1$atqq$/a$9$/b", [this](std::string v) { + return Format::wparse(ToWString(std::move(v))); + })); p.add("alternative_header_second_line_format", assign_default<std::string>( - new_header_second_line, "{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}", adjust_and_validate_format - )); + new_header_second_line, "{{$4$b%a$/b$9}{ - $7%b$9}{ ($4%y$9)}}|{%D}", [this](std::string v) { + return Format::wparse(ToWString(std::move(v))); + })); p.add("now_playing_prefix", buffer( now_playing_prefix, NC::Buffer::init(NC::Format::Bold), [this](NC::Buffer buf) { now_playing_prefix_length = wideLength(ToWString(buf.str())); @@ -341,12 +323,13 @@ bool Configuration::read(const std::string &config_path) modified_item_prefix, NC::Buffer::init(NC::Color::Green, "> ", NC::Color::End), id_() )); p.add("song_window_title_format", assign_default<std::string>( - song_window_title_format, "{%a - }{%t}|{%f}", adjust_and_validate_format + song_window_title_format, "{%a - }{%t}|{%f}", Format::parse )); p.add("song_columns_list_format", assign_default<std::string>( columns_format, "(20)[]{a} (6f)[green]{NE} (50)[white]{t|f:Title} (20)[cyan]{b} (7f)[magenta]{l}", [this](std::string v) { - boost::tie(columns, song_in_columns_to_string_format) = generate_columns(v); + columns = generate_columns(v); + song_columns_mode_format = columns_to_format(columns); return v; })); p.add("execute_on_song_change", assign_default( @@ -586,7 +569,7 @@ bool Configuration::read(const std::string &config_path) empty_tag, "<empty>" )); p.add("tags_separator", assign_default( - tags_separator, " | " + MPD::Song::TagsSeparator, " | " )); p.add("tag_editor_extended_numeration", yes_no( tag_editor_extended_numeration, false diff --git a/src/settings.h b/src/settings.h index a9b1fad2..65b3a042 100644 --- a/src/settings.h +++ b/src/settings.h @@ -29,6 +29,7 @@ #include <mpd/client.h> #include "enums.h" +#include "format.h" #include "screen_type.h" #include "strbuffer.h" @@ -61,21 +62,20 @@ struct Configuration std::string visualizer_fifo_path; std::string visualizer_output_name; std::string empty_tag; - std::string tags_separator; - std::string song_list_format; - std::string song_list_format_dollar_free; - std::string song_status_format; - std::string song_status_format_no_colors; - std::string song_window_title_format; - std::string song_library_format; - std::string tag_editor_album_format; - std::string song_in_columns_to_string_format; - std::string browser_sort_format; + + Format::AST<char> song_list_format; + Format::AST<char> song_window_title_format; + Format::AST<char> song_library_format; + Format::AST<char> song_columns_mode_format; + Format::AST<char> browser_sort_format; + Format::AST<char> song_status_format; + Format::AST<wchar_t> song_status_wformat; + Format::AST<wchar_t> new_header_first_line; + Format::AST<wchar_t> new_header_second_line; + std::string external_editor; std::string system_encoding; std::string execute_on_song_change; - std::string new_header_first_line; - std::string new_header_second_line; std::string lastfm_preferred_language; std::wstring progressbar; std::wstring visualizer_chars; diff --git a/src/song.cpp b/src/song.cpp index c7d07ba7..9b8479d1 100644 --- a/src/song.cpp +++ b/src/song.cpp @@ -42,7 +42,9 @@ size_t calc_hash(const char* s, unsigned seed = 0) } -namespace MPD {// +namespace MPD { + +std::string Song::TagsSeparator = " | "; std::string Song::get(mpd_tag_type type, unsigned idx) const { @@ -197,7 +199,7 @@ std::string Song::getPriority(unsigned idx) const return boost::lexical_cast<std::string>(getPrio()); } -std::string MPD::Song::getTags(GetFunction f, const std::string &tags_separator) const +std::string MPD::Song::getTags(GetFunction f) const { assert(m_song); unsigned idx = 0; @@ -205,7 +207,7 @@ std::string MPD::Song::getTags(GetFunction f, const std::string &tags_separator) for (std::string tag; !(tag = (this->*f)(idx)).empty(); ++idx) { if (!result.empty()) - result += tags_separator; + result += TagsSeparator; result += tag; } return result; @@ -259,13 +261,6 @@ bool Song::empty() const return m_song.get() == 0; } -std::string Song::toString(const std::string &fmt, const std::string &tags_separator, const std::string &escape_chars) const -{ - assert(m_song); - std::string::const_iterator it = fmt.begin(); - return ParseFormat(it, tags_separator, escape_chars); -} - std::string Song::ShowTime(unsigned length) { int hours = length/3600; @@ -282,126 +277,4 @@ std::string Song::ShowTime(unsigned length) return result; } -void MPD::Song::validateFormat(const std::string &fmt) -{ - int braces = 0; - for (std::string::const_iterator it = fmt.begin(); it != fmt.end(); ++it) - { - if (*it == '{') - ++braces; - else if (*it == '}') - --braces; - } - if (braces) - 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])) - throw std::runtime_error( - (boost::format("invalid character at position %1%: %2%") % (i+1) % fmt[i]).str() - ); - } -} - -std::string Song::ParseFormat(std::string::const_iterator &it, const std::string &tags_separator, - const std::string &escape_chars) const -{ - std::string result; - bool has_some_tags = 0; - MPD::Song::GetFunction get_fun = 0; - while (*++it != '}') - { - while (*it == '{') - { - std::string tags = ParseFormat(it, tags_separator, escape_chars); - if (!tags.empty()) - { - has_some_tags = 1; - result += tags; - } - } - if (*it == '}') - break; - - if (*it == '%') - { - size_t delimiter = 0; - if (isdigit(*++it)) - { - delimiter = atol(&*it); - while (isdigit(*++it)) { } - } - - if (*it == '%') - { - result += *it; - get_fun = 0; - } - else - get_fun = charToGetFunction(*it); - - if (get_fun) - { - std::string tag = getTags(get_fun, tags_separator); - if (!escape_chars.empty()) // prepend format escape character to all given chars to escape - { - for (size_t i = 0; i < escape_chars.length(); ++i) - for (size_t j = 0; (j = tag.find(escape_chars[i], j)) != std::string::npos; j += 2) - tag.replace(j, 1, std::string(1, FormatEscapeCharacter) + escape_chars[i]); - } - if (!tag.empty() && (get_fun != &MPD::Song::getLength || getDuration() > 0)) - { - if (delimiter && tag.size() > delimiter) - { - // Shorten length string by just chopping off the tail - if (get_fun == &MPD::Song::getLength) - tag = tag.substr(0, delimiter); - else - tag = ToString(wideShorten(ToWString(tag), delimiter)); - } - has_some_tags = 1; - result += tag; - } - else - break; - } - } - else - result += *it; - } - int brace_counter = 0; - if (*it != '}' || !has_some_tags) - { - for (; *it != '}' || brace_counter; ++it) - { - if (*it == '{') - ++brace_counter; - else if (*it == '}') - --brace_counter; - } - if (*++it == '|') - return ParseFormat(++it, tags_separator, escape_chars); - else - return ""; - } - else - { - if (*(it+1) == '|') - { - for (; *it != '}' || *(it+1) == '|' || brace_counter; ++it) - { - if (*it == '{') - ++brace_counter; - else if (*it == '}') - --brace_counter; - } - } - ++it; - return result; - } -} - } @@ -72,7 +72,7 @@ struct Song virtual std::string getLength(unsigned idx = 0) const; virtual std::string getPriority(unsigned idx = 0) const; - virtual std::string getTags(GetFunction f, const std::string &tags_separator) const; + virtual std::string getTags(GetFunction f) const; virtual unsigned getDuration() const; virtual unsigned getPosition() const; @@ -85,9 +85,6 @@ struct Song virtual bool empty() const; - virtual std::string toString(const std::string &fmt, const std::string &tags_separator, - const std::string &escape_chars = "") const; - bool operator==(const Song &rhs) const { if (m_hash != rhs.m_hash) return false; @@ -102,14 +99,10 @@ struct Song const char *c_uri() const { return m_song ? mpd_song_get_uri(m_song.get()) : ""; } static std::string ShowTime(unsigned length); - static void validateFormat(const std::string &fmt); - - static const char FormatEscapeCharacter = 1; + + static std::string TagsSeparator; private: - std::string ParseFormat(std::string::const_iterator &it, const std::string &tags_separator, - const std::string &escape_chars) const; - std::shared_ptr<mpd_song> m_song; size_t m_hash; }; diff --git a/src/song_info.cpp b/src/song_info.cpp index fa193129..36ecbb9f 100644 --- a/src/song_info.cpp +++ b/src/song_info.cpp @@ -144,6 +144,6 @@ void SongInfo::PrepareSong(MPD::Song &s) for (const Metadata *m = Tags; m->Name; ++m) { w << NC::Format::Bold << '\n' << m->Name << ": " << NC::Format::NoBold; - ShowTag(w, s.getTags(m->Get, Config.tags_separator)); + ShowTag(w, s.getTags(m->Get)); } } diff --git a/src/sort_playlist.cpp b/src/sort_playlist.cpp index 33fc8258..4a460698 100644 --- a/src/sort_playlist.cpp +++ b/src/sort_playlist.cpp @@ -171,8 +171,8 @@ void SortPlaylistDialog::sort() const auto song_cmp = [this, &cmp](const MPD::Song &a, const MPD::Song &b) -> bool { for (auto it = w.beginV(); it->item().second; ++it) { - int res = cmp(a.getTags(it->item().second, Config.tags_separator), - b.getTags(it->item().second, Config.tags_separator)); + int res = cmp(a.getTags(it->item().second), + b.getTags(it->item().second)); if (res != 0) return res < 0; } diff --git a/src/status.cpp b/src/status.cpp index 17ce036c..cd4ea7f3 100644 --- a/src/status.cpp +++ b/src/status.cpp @@ -79,7 +79,7 @@ int m_volume; void drawTitle(const MPD::Song &np) { assert(!np.empty()); - windowTitle(np.toString(Config.song_window_title_format, Config.tags_separator)); + windowTitle(Format::stringify<char>(Config.song_window_title_format, &np)); } std::string playerStateToString(MPD::PlayerState ps) @@ -591,7 +591,7 @@ void Status::Changes::elapsedTime(bool update_elapsed) tracklength += "]"; } NC::WBuffer np_song; - stringToBuffer(ToWString(Charset::utf8ToLocale(np.toString(Config.song_status_format, Config.tags_separator, "$"))), np_song); + Format::print(Config.song_status_wformat, np_song, &np); *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; @@ -619,8 +619,8 @@ void Status::Changes::elapsedTime(bool update_elapsed) } 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); + Format::print(Config.new_header_first_line, first, &np); + Format::print(Config.new_header_second_line, second, &np); size_t first_len = wideLength(first.str()); size_t first_margin = (std::max(tracklength.length()+1, VolumeState.length()))*2; diff --git a/src/tag_editor.cpp b/src/tag_editor.cpp index e64fb922..3e4c7eb7 100644 --- a/src/tag_editor.cpp +++ b/src/tag_editor.cpp @@ -490,17 +490,17 @@ void TagEditor::enterPressed() { Statusbar::ScopedLock slock; Statusbar::put() << NC::Format::Bold << TagTypes->current()->value() << NC::Format::NoBold << ": "; - std::string new_tag = wFooter->prompt(Tags->current()->value().getTags(get, Config.tags_separator)); + std::string new_tag = wFooter->prompt(Tags->current()->value().getTags(get)); for (auto it = EditedSongs.begin(); it != EditedSongs.end(); ++it) - (*it)->setTags(set, new_tag, Config.tags_separator); + (*it)->setTags(set, new_tag); } else if (w == Tags) { Statusbar::ScopedLock slock; Statusbar::put() << NC::Format::Bold << TagTypes->current()->value() << NC::Format::NoBold << ": "; - std::string new_tag = wFooter->prompt(Tags->current()->value().getTags(get, Config.tags_separator)); - if (new_tag != Tags->current()->value().getTags(get, Config.tags_separator)) - Tags->current()->value().setTags(set, new_tag, Config.tags_separator); + std::string new_tag = wFooter->prompt(Tags->current()->value().getTags(get)); + if (new_tag != Tags->current()->value().getTags(get)) + Tags->current()->value().setTags(set, new_tag); Tags->scroll(NC::Scroll::Down); } } @@ -1053,7 +1053,7 @@ MPD::MutableSong::SetFunction IntoSetFunction(char c) std::string GenerateFilename(const MPD::MutableSong &s, const std::string &pattern) { - std::string result = s.toString(pattern, Config.tags_separator); + std::string result = Format::stringify<char>(Format::parse(pattern), &s); removeInvalidCharsFromFilename(result, Config.generate_win32_compatible_filenames); return result; } @@ -1105,7 +1105,7 @@ std::string ParseFilename(MPD::MutableSong &s, std::string mask, bool preview) { MPD::MutableSong::SetFunction set = IntoSetFunction(it->first); if (set) - s.setTags(set, it->second, Config.tags_separator); + s.setTags(set, it->second); } else result << "%" << it->first << ": " << it->second << "\n"; diff --git a/src/tags.cpp b/src/tags.cpp index 3d8feef6..3610f80c 100644 --- a/src/tags.cpp +++ b/src/tags.cpp @@ -145,7 +145,7 @@ void writeID3v2Tags(const MPD::MutableSong &s, TagLib::ID3v2::Tag *tag) auto frame = new TagLib::ID3v2::CommentsFrame(TagLib::String::UTF8); // apparently there can't be multiple comments, // so if there is more than one, join them. - frame->setText(join(list, TagLib::String(Config.tags_separator, TagLib::String::UTF8))); + frame->setText(join(list, TagLib::String(MPD::Song::TagsSeparator, TagLib::String::UTF8))); tag->addFrame(frame); } else diff --git a/src/tiny_tag_editor.cpp b/src/tiny_tag_editor.cpp index 4cacb4f1..82e19060 100644 --- a/src/tiny_tag_editor.cpp +++ b/src/tiny_tag_editor.cpp @@ -107,10 +107,10 @@ void TinyTagEditor::enterPressed() size_t pos = option-8; Statusbar::put() << NC::Format::Bold << SongInfo::Tags[pos].Name << ": " << NC::Format::NoBold; itsEdited.setTags(SongInfo::Tags[pos].Set, Global::wFooter->prompt( - itsEdited.getTags(SongInfo::Tags[pos].Get, Config.tags_separator)), Config.tags_separator); + itsEdited.getTags(SongInfo::Tags[pos].Get))); w.at(option).value().clear(); w.at(option).value() << NC::Format::Bold << SongInfo::Tags[pos].Name << ':' << NC::Format::NoBold << ' '; - ShowTag(w.at(option).value(), itsEdited.getTags(SongInfo::Tags[pos].Get, Config.tags_separator)); + ShowTag(w.at(option).value(), itsEdited.getTags(SongInfo::Tags[pos].Get)); } else if (option == 20) { @@ -226,7 +226,7 @@ bool TinyTagEditor::getTags() for (const SongInfo::Metadata *m = SongInfo::Tags; m->Name; ++m, ++pos) { w.at(pos).value() << NC::Format::Bold << m->Name << ":" << NC::Format::NoBold << ' '; - ShowTag(w.at(pos).value(), itsEdited.getTags(m->Get, Config.tags_separator)); + ShowTag(w.at(pos).value(), itsEdited.getTags(m->Get)); } w.at(20).value() << NC::Format::Bold << "Filename:" << NC::Format::NoBold << ' ' << itsEdited.getName(); diff --git a/src/utility/comparators.cpp b/src/utility/comparators.cpp index 02a81453..bfb4b2ce 100644 --- a/src/utility/comparators.cpp +++ b/src/utility/comparators.cpp @@ -81,8 +81,8 @@ bool LocaleBasedItemSorting::operator()(const MPD::Item &a, const MPD::Item &b) result = m_cmp(a.playlist().path(), b.playlist().path()); break; case MPD::Item::Type::Song: - result = m_cmp(a.song().toString(Config.browser_sort_format, Config.tags_separator), - b.song().toString(Config.browser_sort_format, Config.tags_separator)); + result = m_cmp(Format::stringify<char>(Config.browser_sort_format, &a.song()), + Format::stringify<char>(Config.browser_sort_format, &b.song())); break; } break; diff --git a/src/utility/type_conversions.cpp b/src/utility/type_conversions.cpp index c20943f4..1975b7e9 100644 --- a/src/utility/type_conversions.cpp +++ b/src/utility/type_conversions.cpp @@ -203,7 +203,7 @@ MPD::Song::GetFunction charToGetFunction(char c) case 'P': return &MPD::Song::getPriority; default: - return 0; + return nullptr; } } diff --git a/src/utility/wide_string.cpp b/src/utility/wide_string.cpp index a9595e48..ee1193ca 100644 --- a/src/utility/wide_string.cpp +++ b/src/utility/wide_string.cpp @@ -18,7 +18,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include <boost/locale/encoding.hpp> +#include <boost/locale/encoding_utf.hpp> #include <cassert> #include "utility/wide_string.h" |