diff --git a/include/nana/basic_types.hpp b/include/nana/basic_types.hpp index 3fb77594..9a2d85f2 100644 --- a/include/nana/basic_types.hpp +++ b/include/nana/basic_types.hpp @@ -306,8 +306,16 @@ namespace nana color(unsigned red, unsigned green, unsigned blue); color(unsigned red, unsigned green, unsigned blue, double alpha); + /// Initializes the color with a CSS-like rgb string. + color(std::string css_rgb); + color& alpha(double); ///< Sets alpha channel color& from_rgb(unsigned red, unsigned green, unsigned blue); ///< immutable alpha channel + + /// Sets color with a HSL value. + /// @param hue in range of [0, 360] + /// @param saturation in range of [0, 1] + /// @param lightness in range of [0, 1] color& from_hsl(double hue, double saturation, double lightness); ///< immutable alpha channel color blend(const color& bgcolor, bool ignore_bgcolor_alpha) const; diff --git a/include/nana/deploy.hpp b/include/nana/deploy.hpp index 66fe0cfe..d417d06d 100644 --- a/include/nana/deploy.hpp +++ b/include/nana/deploy.hpp @@ -42,6 +42,14 @@ namespace nana std::size_t strlen(const char_t* str); double strtod(const char_t* str, char_t** endptr); char_t* strcpy(char_t* dest, const char_t* source); + + //Workaround for no implemenation of std::stoi in MinGW. + int stoi(const std::string&, std::size_t * pos = nullptr, int base = 10); + int stoi(const std::wstring&, std::size_t* pos = nullptr, int base = 10); + + //Workaround for no implemenation of std::stod in MinGW. + double stod(const std::string&, std::size_t * pos = nullptr); + double stod(const std::wstring&, std::size_t* pos = nullptr); } #if defined(NANA_WINDOWS) diff --git a/source/basic_types.cpp b/source/basic_types.cpp index dcafa12f..2ccf119e 100644 --- a/source/basic_types.cpp +++ b/source/basic_types.cpp @@ -11,6 +11,7 @@ */ #include +#include namespace nana { @@ -58,6 +59,168 @@ namespace nana a_ = 1.0; } + //Initializes the color with a CSS-like string + //contributor: BigDave(mortis2007 at hotmail co uk) + //date: February 3, 2015 + //maintainor: Jinhao, extended the support of CSS-spec + color::color(std::string css_color) + : a_(1.0) + { + const char * excpt_what = "color: invalid rgb format"; + + auto pos = css_color.find_first_not_of(' '); + if (pos == css_color.npos) + throw std::invalid_argument(excpt_what); + + if ('#' == css_color[pos]) + { + if (css_color.size() < pos + 4) + throw std::invalid_argument(excpt_what); + + auto endpos = css_color.find_first_not_of("0123456789abcdefABCDEF", pos + 1); + if (endpos == css_color.npos) + endpos = static_cast(css_color.size()); + + if ((endpos - pos != 4) && (endpos - pos != 7)) + throw std::invalid_argument(excpt_what); + + auto n = ::nana::stoi(css_color.substr(pos + 1, endpos - pos - 1), nullptr, 16); + + if (endpos - pos == 4) + { + r_ = ((0xF00 & n) >> 4) | ((0xF00 & n) >> 8); + g_ = (0xF0 & n) | ((0xF0 & n) >> 4); + b_ = (0xF & n) | ((0xF & n) << 4); + } + else + { + r_ = (0xFF0000 & n) >> 16; + g_ = (0xFF00 & n) >> 8; + b_ = (0xFF & n); + } + + return; + } + + std::transform(css_color.begin(), css_color.end(), css_color.begin(), std::tolower); + auto endpos = css_color.find(' ', pos + 1); + if (endpos == css_color.npos) + endpos = css_color.size(); + + if ((endpos - pos == 11) && (css_color.substr(pos, 11) == "transparent")) + { + r_ = 0; + g_ = 0; + b_ = 0; + a_ = 0; + return; + } + + auto type_end = css_color.find_first_of(" (", pos + 1); + + if (type_end == css_color.npos || ((type_end - pos != 3) && (type_end - pos != 4))) //rgb/hsl = 3, rgba/hsla = 4 + throw std::invalid_argument(excpt_what); + + bool has_alpha = false; + if (type_end - pos == 4) //maybe rgba/hsla + { + if (css_color[pos + 3] != 'a') + throw std::invalid_argument(excpt_what); + has_alpha = true; + } + + std::regex pat; + std::regex_iterator i, end; + auto type_name = css_color.substr(pos, 3); + if ("rgb" == type_name) + { + pat.assign("(\\d*\\.)?\\d+\\%?"); + i = std::regex_iterator(css_color.begin() + pos, css_color.end(), pat); + + if (i == end) + throw std::invalid_argument(excpt_what); + + std::vector rgb; + + rgb.emplace_back(i->str()); + + bool is_real; + if (is_real = (rgb.back().back() == '%')) + pat.assign("(\\d*\\.)?\\d+\\%"); + else + pat.assign("\\d+"); + + for (++i; i != end; ++i) + { + rgb.emplace_back(i->str()); + if (rgb.size() == 3) + break; + } + + if (rgb.size() != 3) + throw std::invalid_argument(excpt_what); + + if (is_real) + { + auto pr = ::nana::stod(rgb[0].substr(0, rgb[0].size() - 1)); + r_ = (pr > 100 ? 255.0 : 2.55 * pr); + + pr = ::nana::stod(rgb[1].substr(0, rgb[1].size() - 1)); + g_ = (pr > 100 ? 255.0 : 2.55 * pr); + + pr = ::nana::stod(rgb[2].substr(0, rgb[2].size() - 1)); + b_ = (pr > 100 ? 255.0 : 2.55 * pr); + } + else + { + r_ = ::nana::stod(rgb[0]); + if (r_ > 255.0) r_ = 255; + + g_ = ::nana::stod(rgb[1]); + if (g_ > 255.0) g_ = 255; + + b_ = ::nana::stod(rgb[2]); + if (b_ > 255.0) b_ = 255; + } + } + else if ("hsl" == type_name) + { + pat.assign("(\\d*\\.)?\\d+"); + i = std::regex_iterator(css_color.begin() + pos, css_color.end(), pat); + + if (i == end) + throw std::invalid_argument(excpt_what); + + auto h = ::nana::stod(i->str()); + + pat.assign("(\\d*\\.)?\\d+\\%"); + + if (++i == end) + throw std::invalid_argument(excpt_what); + + auto str = i->str(); + auto s = ::nana::stod(str.substr(0, str.size() - 1)); + + if (++i == end) + throw std::invalid_argument(excpt_what); + + str = i->str(); + auto l = ::nana::stod(str.substr(0, str.size() - 1)); + + from_hsl(h, s / 100, l / 100); + } + else + throw std::invalid_argument(excpt_what); //invalid color type + + if (has_alpha) + { + pat.assign("(\\d*\\.)?\\d+"); + if (++i == end) + throw std::invalid_argument(excpt_what); //invalid alpha value + a_ = ::nana::stod(i->str()); + } + } + color& color::from_rgb(unsigned red, unsigned green, unsigned blue) { r_ = red; @@ -97,6 +260,7 @@ namespace nana double var1 = 2.0 * lightness - var2; + hue /= 360; r_ = 255.0 * rgb_from_hue(var1, var2, hue + 0.33333); g_ = 255.0 * rgb_from_hue(var1, var2, hue); b_ = 255.0 * rgb_from_hue(var1, var2, hue - 0.33333); diff --git a/source/deploy.cpp b/source/deploy.cpp index d326ba7c..de83232d 100644 --- a/source/deploy.cpp +++ b/source/deploy.cpp @@ -12,7 +12,8 @@ */ #include - +#include +#include #if defined(NANA_WINDOWS) #include #elif defined(NANA_LINUX) @@ -49,6 +50,88 @@ namespace nana #endif } + int stoi(const std::string& str, std::size_t * pos, int base) + { +#if defined(NANA_MINGW) + auto sptr = str.c_str(); + char *end; + errno = 0; + auto result = std::strtol(sptr, &end, base); + + if (sptr == end) + throw std::invalid_argument("invalid stoi argument"); + if (errno == ERANGE) + throw std::out_of_range("stoi argument out of range"); + + if (pos) + *pos = (std::size_t)(end - sptr); + return ((int)result); +#else + return std::stoi(str, pos, base); +#endif + } + + int stoi(const std::wstring& str, std::size_t* pos, int base) + { +#if defined(NANA_MINGW) + auto sptr = str.data(); + wchar_t *end; + errno = 0; + auto result = std::wcstol(sptr, &end, base); + + if (sptr == end) + throw std::invalid_argument("invalid stoi argument"); + if (errno == ERANGE) + throw std::out_of_range("stoi argument out of range"); + + if (pos) + *pos = (std::size_t)(end - sptr); + return ((int)result); +#else + return std::stoi(str, pos, base); +#endif + } + + double stod(const std::string& str, std::size_t * pos) + { +#ifdef NANA_MINGW + auto *ptr = str.data(); + errno = 0; + char *end; + auto result = std::strtod(ptr, &end); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stod argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; +#else + return std::stod(str, pos); +#endif + } + + double stod(const std::wstring& str, std::size_t* pos) + { +#ifdef NANA_MINGW + auto *ptr = str.data(); + errno = 0; + wchar_t *end; + auto result = std::wcstod(ptr, &end); + + if (ptr == end) + throw std::invalid_argument("invalid stod argument"); + if (errno == ERANGE) + throw std::out_of_range("stod argument out of range"); + if (pos) + *pos = (std::size_t)(end - ptr); + return result; +#else + return std::stod(str, pos); +#endif + } + bool is_incomplete(const nana::string& str, unsigned pos) { #ifndef NANA_UNICODE diff --git a/source/gui/widgets/spinbox.cpp b/source/gui/widgets/spinbox.cpp index 262765de..5981d0ea 100644 --- a/source/gui/widgets/spinbox.cpp +++ b/source/gui/widgets/spinbox.cpp @@ -611,22 +611,12 @@ namespace nana int spinbox::to_int() const { - //std::stoi is not defined by MinGW - std::wstringstream ss; - ss << value(); - int n = 0; - ss >> n; - return n; + return ::nana::stoi(value()); } double spinbox::to_double() const { - //std::stod is not defined by MinGW - std::wstringstream ss; - ss << value(); - double d = 0; - ss >> d; - return d; + return ::nana::stod(value()); } void spinbox::set_accept(std::function pred) diff --git a/source/internationalization.cpp b/source/internationalization.cpp index 2b868a87..30908ce7 100644 --- a/source/internationalization.cpp +++ b/source/internationalization.cpp @@ -343,12 +343,7 @@ namespace nana erase_n = str.size() - offset; //If there is not a parameter for %argNNN, the %argNNN will be erased. - - //a workaround, MinGW does not provide std::stoi - std::wstringstream ss; - std::size_t arg; - ss<>arg; + std::size_t arg = static_cast(::nana::stoi(str.substr(offset + 4, arg_n))); if (arg_strs && arg < arg_strs->size()) str.replace(offset, erase_n, (*arg_strs)[arg]);