diff options
Diffstat (limited to 'src/util/ucompose.hpp')
-rw-r--r-- | src/util/ucompose.hpp | 452 |
1 files changed, 452 insertions, 0 deletions
diff --git a/src/util/ucompose.hpp b/src/util/ucompose.hpp new file mode 100644 index 0000000..e0d2fc2 --- /dev/null +++ b/src/util/ucompose.hpp @@ -0,0 +1,452 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/** @file + * TODO: insert short description here + *//* + * Authors: see git history + * + * Copyright (C) 2006 Authors + * Released under GNU LGPL v2.1+, read the file 'COPYING' for more information. + */ +/* Defines String::ucompose(fmt, arg...) for easy, i18n-friendly + * composition of strings with Gtkmm >= 1.3.* (see www.gtkmm.org). + * Uses Glib::ustring instead of std::string which doesn't work with + * Gtkmm due to character encoding troubles with stringstreams. + * + * Version 1.0.4. + * + * Copyright (c) 2002, 03, 04 Ole Laursen <olau@hardworking.dk>. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA. + */ + +// +// Basic usage is like +// +// String::ucompose("This is a %1x%2 matrix.", rows, cols); +// +// See http://www.cs.aau.dk/~olau/compose/ or the included +// README.compose for more details. +// + +#ifndef STRING_UCOMPOSE_HPP +#define STRING_UCOMPOSE_HPP + +#include <glibmm/ustring.h> +#include <glibmm/convert.h> + +#include <stdexcept> +#include <sstream> +#include <string> +#include <list> +#include <map> // for multimap + +namespace UStringPrivate +{ + // the actual composition class - using String::ucompose is cleaner, so we + // hide it here + class Composition + { + public: + // initialize and prepare format string on the form "text %1 text %2 etc." + explicit Composition(std::string fmt); + + // supply an replacement argument starting from %1 + template <typename T> + Composition &arg(const T &obj); + + // compose and return string + Glib::ustring str() const; + + private: + + //This is standard, not GCC-specific like wostringstream + std::basic_ostringstream<wchar_t> os; + + int arg_no; + + // we store the output as a list - when the output string is requested, the + // list is concatenated to a string; this way we can keep iterators into + // the list instead of into a string where they're possibly invalidated + // when inserting a specification string + typedef std::list<std::string> output_list; + output_list output; + + // the initial parse of the format string fills in the specification map + // with positions for each of the various %?s + typedef std::multimap<int, output_list::iterator> specification_map; + specification_map specs; + + template <typename T> + std::string stringify(T obj); + }; + + // helper for converting spec string numbers + inline int char_to_int(char c) + { + switch (c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + default: return -1000; + } + } + + inline bool is_number(int n) + { + switch (n) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return true; + + default: + return false; + } + } + + template <typename T> + inline std::string Composition::stringify(T obj) + { + os << obj; + + std::wstring str = os.str(); + + return Glib::convert(std::string(reinterpret_cast<const char *>(str.data()), + str.size() * sizeof(wchar_t)), + "UTF-8", "WCHAR_T"); + } + + // specialisations for the common string types + template <> + inline std::string + Composition::stringify<std::string>(std::string obj) + { + return obj; + } + + template <> + inline std::string + Composition::stringify<Glib::ustring>(Glib::ustring obj) + { + return obj; + } + + template <> + inline std::string + Composition::stringify<const char *>(const char *obj) + { + return obj; + } + + // implementation of class Composition + template <typename T> + inline Composition &Composition::arg(const T &obj) + { + Glib::ustring rep = stringify(obj); + + if (!rep.empty()) { // manipulators don't produce output + for (specification_map::const_iterator i = specs.lower_bound(arg_no), + end = specs.upper_bound(arg_no); i != end; ++i) { + output_list::iterator pos = i->second; + ++pos; + + output.insert(pos, rep); + } + + os.str(std::wstring()); + //os.clear(); + ++arg_no; + } + + return *this; + } + + inline Composition::Composition(std::string fmt) + : arg_no(1) + { +#if __GNUC__ >= 3 + try { + os.imbue(std::locale("")); // use the user's locale for the stream + } + catch (std::runtime_error& e) { // fallback to classic if it failed + os.imbue(std::locale::classic()); + } +#endif + std::string::size_type b = 0, i = 0; + + // fill in output with the strings between the %1 %2 %3 etc. and + // fill in specs with the positions + while (i < fmt.length()) { + if (fmt[i] == '%' && i + 1 < fmt.length()) { + if (fmt[i + 1] == '%') { // catch %% + fmt.replace(i, 2, "%"); + ++i; + } + else if (is_number(fmt[i + 1])) { // aha! a spec! + // save string + output.push_back(fmt.substr(b, i - b)); + + int n = 1; // number of digits + int spec_no = 0; + + do { + spec_no += char_to_int(fmt[i + n]); + spec_no *= 10; + ++n; + } while (i + n < fmt.length() && is_number(fmt[i + n])); + + spec_no /= 10; + output_list::iterator pos = output.end(); + --pos; // safe since we have just inserted a string + + specs.insert(specification_map::value_type(spec_no, pos)); + + // jump over spec string + i += n; + b = i; + } + else + ++i; + } + else + ++i; + } + + if (i - b > 0) // add the rest of the string + output.push_back(fmt.substr(b, i - b)); + } + + inline Glib::ustring Composition::str() const + { + // assemble string + std::string str; + + for (const auto & i : output) + str += i; + + return str; + } +} + + +namespace String +{ + // a series of functions which accept a format string on the form "text %1 + // more %2 less %3" and a number of templated parameters and spits out the + // composited string + template <typename T1> + inline Glib::ustring ucompose(const Glib::ustring &fmt, const T1 &o1) + { + UStringPrivate::Composition c(fmt); + c.arg(o1); + return c.str(); + } + + template <typename T1, typename T2> + inline Glib::ustring ucompose(const Glib::ustring &fmt, + const T1 &o1, const T2 &o2) + { + UStringPrivate::Composition c(fmt); + c.arg(o1).arg(o2); + return c.str(); + } + + template <typename T1, typename T2, typename T3> + inline Glib::ustring ucompose(const Glib::ustring &fmt, + const T1 &o1, const T2 &o2, const T3 &o3) + { + UStringPrivate::Composition c(fmt); + c.arg(o1).arg(o2).arg(o3); + return c.str(); + } + + template <typename T1, typename T2, typename T3, typename T4> + inline Glib::ustring ucompose(const Glib::ustring &fmt, + const T1 &o1, const T2 &o2, const T3 &o3, + const T4 &o4) + { + UStringPrivate::Composition c(fmt); + c.arg(o1).arg(o2).arg(o3).arg(o4); + return c.str(); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5> + inline Glib::ustring ucompose(const Glib::ustring &fmt, + const T1 &o1, const T2 &o2, const T3 &o3, + const T4 &o4, const T5 &o5) + { + UStringPrivate::Composition c(fmt); + c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5); + return c.str(); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6> + inline Glib::ustring ucompose(const Glib::ustring &fmt, + const T1 &o1, const T2 &o2, const T3 &o3, + const T4 &o4, const T5 &o5, const T6 &o6) + { + UStringPrivate::Composition c(fmt); + c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6); + return c.str(); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7> + inline Glib::ustring ucompose(const Glib::ustring &fmt, + const T1 &o1, const T2 &o2, const T3 &o3, + const T4 &o4, const T5 &o5, const T6 &o6, + const T7 &o7) + { + UStringPrivate::Composition c(fmt); + c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7); + return c.str(); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8> + inline Glib::ustring ucompose(const Glib::ustring &fmt, + const T1 &o1, const T2 &o2, const T3 &o3, + const T4 &o4, const T5 &o5, const T6 &o6, + const T7 &o7, const T8 &o8) + { + UStringPrivate::Composition c(fmt); + c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8); + return c.str(); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9> + inline Glib::ustring ucompose(const Glib::ustring &fmt, + const T1 &o1, const T2 &o2, const T3 &o3, + const T4 &o4, const T5 &o5, const T6 &o6, + const T7 &o7, const T8 &o8, const T9 &o9) + { + UStringPrivate::Composition c(fmt); + c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9); + return c.str(); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10> + inline Glib::ustring ucompose(const Glib::ustring &fmt, + const T1 &o1, const T2 &o2, const T3 &o3, + const T4 &o4, const T5 &o5, const T6 &o6, + const T7 &o7, const T8 &o8, const T9 &o9, + const T10 &o10) + { + UStringPrivate::Composition c(fmt); + c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9) + .arg(o10); + return c.str(); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11> + inline Glib::ustring ucompose(const Glib::ustring &fmt, + const T1 &o1, const T2 &o2, const T3 &o3, + const T4 &o4, const T5 &o5, const T6 &o6, + const T7 &o7, const T8 &o8, const T9 &o9, + const T10 &o10, const T11 &o11) + { + UStringPrivate::Composition c(fmt); + c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9) + .arg(o10).arg(o11); + return c.str(); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12> + inline Glib::ustring ucompose(const Glib::ustring &fmt, + const T1 &o1, const T2 &o2, const T3 &o3, + const T4 &o4, const T5 &o5, const T6 &o6, + const T7 &o7, const T8 &o8, const T9 &o9, + const T10 &o10, const T11 &o11, const T12 &o12) + { + UStringPrivate::Composition c(fmt); + c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9) + .arg(o10).arg(o11).arg(o12); + return c.str(); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13> + inline Glib::ustring ucompose(const Glib::ustring &fmt, + const T1 &o1, const T2 &o2, const T3 &o3, + const T4 &o4, const T5 &o5, const T6 &o6, + const T7 &o7, const T8 &o8, const T9 &o9, + const T10 &o10, const T11 &o11, const T12 &o12, + const T13 &o13) + { + UStringPrivate::Composition c(fmt); + c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9) + .arg(o10).arg(o11).arg(o12).arg(o13); + return c.str(); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14> + inline Glib::ustring ucompose(const Glib::ustring &fmt, + const T1 &o1, const T2 &o2, const T3 &o3, + const T4 &o4, const T5 &o5, const T6 &o6, + const T7 &o7, const T8 &o8, const T9 &o9, + const T10 &o10, const T11 &o11, const T12 &o12, + const T13 &o13, const T14 &o14) + { + UStringPrivate::Composition c(fmt); + c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9) + .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14); + return c.str(); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, + typename T15> + inline Glib::ustring ucompose(const Glib::ustring &fmt, + const T1 &o1, const T2 &o2, const T3 &o3, + const T4 &o4, const T5 &o5, const T6 &o6, + const T7 &o7, const T8 &o8, const T9 &o9, + const T10 &o10, const T11 &o11, const T12 &o12, + const T13 &o13, const T14 &o14, const T15 &o15) + { + UStringPrivate::Composition c(fmt); + c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9) + .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14).arg(o15); + return c.str(); + } +} + + +#endif // STRING_UCOMPOSE_HPP |