diff --git a/src/addon/manager_ui.cpp b/src/addon/manager_ui.cpp index 57f3b043df6..901c4ed4b6c 100644 --- a/src/addon/manager_ui.cpp +++ b/src/addon/manager_ui.cpp @@ -664,10 +664,10 @@ void show_addons_manager_dialog(display& disp, addons_client& client, addons_lis // Versions are too important in upgrades mode, so don't // truncate them then. if(!updates_only) { - utils::truncate_as_wstring(display_version, 12); + utils::truncate_as_ucs4string(display_version, 12); if(state == ADDON_INSTALLED_UPGRADABLE || state == ADDON_INSTALLED_OUTDATED) { - utils::truncate_as_wstring(display_old_version, 12); + utils::truncate_as_ucs4string(display_old_version, 12); if(state == ADDON_INSTALLED_UPGRADABLE) { display_version = diff --git a/src/construct_dialog.cpp b/src/construct_dialog.cpp index 391430a94f4..d288953ba36 100644 --- a/src/construct_dialog.cpp +++ b/src/construct_dialog.cpp @@ -910,8 +910,8 @@ void filter_textbox::delete_item(int selection) { /* dialog_.set_menu_items(filtered_items_); */ } -void filter_textbox::handle_text_changed(const wide_string& text) { - const std::vector words = utils::split(utils::wstring_to_string(text),' '); +void filter_textbox::handle_text_changed(const ucs4_string& text) { + const std::vector words = utils::split(utils::ucs4string_to_string(text),' '); if (words == last_words) return; last_words = words; diff --git a/src/construct_dialog.hpp b/src/construct_dialog.hpp index 9d47806d20d..72046c592c5 100644 --- a/src/construct_dialog.hpp +++ b/src/construct_dialog.hpp @@ -145,7 +145,7 @@ private: std::vector last_words; size_t header_row_; gui::dialog& dialog_; - virtual void handle_text_changed(const wide_string& text); + virtual void handle_text_changed(const ucs4_string& text); }; class dialog_button : public button { diff --git a/src/desktop_util.cpp b/src/desktop_util.cpp index 3e9321a2e06..07fa781aa3b 100644 --- a/src/desktop_util.cpp +++ b/src/desktop_util.cpp @@ -91,10 +91,11 @@ bool open_object(const std::string& path_or_url) LOG_DU << "open_object(): on Win32, will use ShellExecute()\n"; - wide_string wpath = utils::string_to_wstring(path_or_url); - wpath.push_back(wchar_t(0)); // Make wpath NULL-terminated + ucs4_string u4path = utils::string_to_ucs4string(path_or_url); + utf16_string u16path = utils::ucs4string_to_utf16string(u4path); + u16path.push_back(wchar_t(0)); // Make wpath NULL-terminated - const ptrdiff_t res = reinterpret_cast(ShellExecute(NULL, L"open", &wpath.front(), NULL, NULL, SW_SHOW)); + const ptrdiff_t res = reinterpret_cast(ShellExecute(NULL, L"open", &u16path.front(), NULL, NULL, SW_SHOW)); if(res <= 32) { ERR_DU << "open_object(): ShellExecute() failed (" << res << ")\n"; return false; diff --git a/src/dialogs.cpp b/src/dialogs.cpp index 1392fd090a3..0c9a7dc5ab0 100644 --- a/src/dialogs.cpp +++ b/src/dialogs.cpp @@ -924,7 +924,7 @@ std::string load_game_dialog(display& disp, const config& game_config, bool* sel std::vector::const_iterator i; for(i = games.begin(); i != games.end(); ++i) { std::string name = i->name(); - utils::truncate_as_wstring(name, std::min(name.size(), 40)); + utils::truncate_as_ucs4string(name, std::min(name.size(), 40)); std::ostringstream str; str << name << COLUMN_SEPARATOR << util::format_time_summary(i->modified()); diff --git a/src/gui/dialogs/addon_list.cpp b/src/gui/dialogs/addon_list.cpp index d479386cfbe..7301470ddc1 100644 --- a/src/gui/dialogs/addon_list.cpp +++ b/src/gui/dialogs/addon_list.cpp @@ -177,17 +177,17 @@ void taddon_list::pre_show(CVideo& /*video*/, twindow& window) data.insert(std::make_pair("icon", item)); std::string tmp = c["name"]; - utils::truncate_as_wstring(tmp, 20); + utils::truncate_as_ucs4string(tmp, 20); item["label"] = tmp; data.insert(std::make_pair("name", item)); tmp = c["version"].str(); - utils::truncate_as_wstring(tmp, 12); + utils::truncate_as_ucs4string(tmp, 12); item["label"] = tmp; data.insert(std::make_pair("version", item)); tmp = c["author"].str(); - utils::truncate_as_wstring(tmp, 16); + utils::truncate_as_ucs4string(tmp, 16); item["label"] = tmp; data.insert(std::make_pair("author", item)); @@ -213,17 +213,17 @@ void taddon_list::create_campaign(tpane& pane, const config& campaign) data.insert(std::make_pair("icon", item)); std::string tmp = campaign["name"]; - utils::truncate_as_wstring(tmp, 20); + utils::truncate_as_ucs4string(tmp, 20); item["label"] = tmp; data.insert(std::make_pair("name", item)); tmp = campaign["version"].str(); - utils::truncate_as_wstring(tmp, 12); + utils::truncate_as_ucs4string(tmp, 12); item["label"] = tmp; data.insert(std::make_pair("version", item)); tmp = campaign["author"].str(); - utils::truncate_as_wstring(tmp, 16); + utils::truncate_as_ucs4string(tmp, 16); item["label"] = tmp; data.insert(std::make_pair("author", item)); diff --git a/src/gui/widgets/password_box.cpp b/src/gui/widgets/password_box.cpp index 3e2842c2a10..55e0b27c982 100644 --- a/src/gui/widgets/password_box.cpp +++ b/src/gui/widgets/password_box.cpp @@ -40,7 +40,7 @@ namespace size_t get_text_length(const std::string& str) { - return utils::string_to_wstring(str).size(); + return utils::string_to_ucs4string(str).size(); } } // namespace diff --git a/src/gui/widgets/text.cpp b/src/gui/widgets/text.cpp index b96f77ca036..bdf1a05f2db 100644 --- a/src/gui/widgets/text.cpp +++ b/src/gui/widgets/text.cpp @@ -154,9 +154,9 @@ void ttext_::copy_selection(const bool mouse) start -= length; } - const wide_string& wtext = utils::string_to_wstring(text_.text()); - const std::string& text = utils::wstring_to_string( - wide_string(wtext.begin() + start, wtext.begin() + start + length)); + const ucs4_string& wtext = utils::string_to_ucs4string(text_.text()); + const std::string& text = utils::ucs4string_to_string( + ucs4_string(wtext.begin() + start, wtext.begin() + start + length)); copy_to_clipboard(text, mouse); } diff --git a/src/gui/widgets/text_box.cpp b/src/gui/widgets/text_box.cpp index a9b6bd7e243..70143e05820 100644 --- a/src/gui/widgets/text_box.cpp +++ b/src/gui/widgets/text_box.cpp @@ -209,9 +209,9 @@ void ttext_box::delete_selection() } // Update the text, we need to assume it's a wide string. - wide_string tmp = utils::string_to_wstring(get_value()); + ucs4_string tmp = utils::string_to_ucs4string(get_value()); tmp.erase(tmp.begin() + start, tmp.begin() + start + len); - const std::string& text = utils::wstring_to_string(tmp); + const std::string& text = utils::ucs4string_to_string(tmp); set_value(text); set_cursor(start, false); } diff --git a/src/help.cpp b/src/help.cpp index 118ef827a3b..0d095ef1b2f 100644 --- a/src/help.cpp +++ b/src/help.cpp @@ -3346,9 +3346,9 @@ std::string get_first_word(const std::string &s) if (ch == utils::utf8_iterator::end(re)) return re; - wchar_t firstchar = *ch; + ucs4char firstchar = *ch; if (font::is_cjk_char(firstchar)) { - re = utils::wchar_to_string(firstchar); + re = utils::ucs4char_to_string(firstchar); } return re; } diff --git a/src/hotkey/hotkey_item.cpp b/src/hotkey/hotkey_item.cpp index db0039ba346..7e73a26e873 100644 --- a/src/hotkey/hotkey_item.cpp +++ b/src/hotkey/hotkey_item.cpp @@ -360,7 +360,7 @@ void hotkey_item::load_from_config(const config& cfg) return; } - wide_string wkey = utils::string_to_wstring(key); + ucs4_string wkey = utils::string_to_ucs4string(key); // They may really want a specific key on the keyboard: // we assume that any single character keyname is a character. @@ -462,7 +462,7 @@ void hotkey_item::save(config& item) const if (get_hat() >= 0) item["hat"] = get_hat(); if (get_value() >= 0) item["value"] = get_value(); if (get_keycode() >= 0) item["key"] = SDL_GetKeyName(SDLKey(get_keycode())); - if (get_character() >= 0) item["key"] = utils::wchar_to_string(get_character()); + if (get_character() >= 0) item["key"] = utils::ucs4char_to_string(get_character()); if (get_mouse() >= 0) item["mouse"] = get_mouse(); if (get_button() >= 0) item["button"] = get_button(); diff --git a/src/marked-up_text.cpp b/src/marked-up_text.cpp index ae0d5b338a2..c5eccb55875 100644 --- a/src/marked-up_text.cpp +++ b/src/marked-up_text.cpp @@ -261,7 +261,7 @@ bool is_format_char(char c) } } -bool is_cjk_char(const wchar_t c) +bool is_cjk_char(const ucs4char ch) { /** * You can check these range at http://unicode.org/charts/ @@ -270,9 +270,6 @@ bool is_cjk_char(const wchar_t c) * Below are characters that I guess may be used in wesnoth translations. */ - // cast to silence a windows warning (uses only 16bit for wchar_t) - const unsigned int ch = static_cast(c); - //FIXME add range from Japanese-specific and Korean-specific section if you know the characters are used today. if (ch < 0x2e80) return false; // shortcut for common non-CJK @@ -359,7 +356,7 @@ namespace { * CJK 标点符号 (CJK punctuations) * http://www.unicode.org/charts/PDF/U3000.pdf */ -inline bool no_break_after(const wchar_t ch) +inline bool no_break_after(const ucs4char ch) { return /** @@ -380,7 +377,7 @@ inline bool no_break_after(const wchar_t ch) ch == 0x3016 || ch == 0x301a || ch == 0x301d; } -inline bool no_break_before(const wchar_t ch) +inline bool no_break_before(const ucs4char ch) { return /** @@ -417,7 +414,7 @@ inline bool no_break_before(const wchar_t ch) ch == 0x301b || ch == 0x301e; } -inline bool break_before(const wchar_t ch) +inline bool break_before(const ucs4char ch) { if(no_break_before(ch)) return false; @@ -425,7 +422,7 @@ inline bool break_before(const wchar_t ch) return is_cjk_char(ch); } -inline bool break_after(const wchar_t ch) +inline bool break_after(const ucs4char ch) { if(no_break_after(ch)) return false; @@ -459,7 +456,7 @@ std::string word_wrap_text(const std::string& unwrapped_text, int font_size, if(start_of_line) { line_width = 0; format_string.clear(); - while(ch != end && *ch < static_cast(0x100) + while(ch != end && *ch < static_cast(0x100) && is_format_char(*ch) && !ch.next_is_end()) { format_string.append(ch.substr().first, ch.substr().second); @@ -482,7 +479,7 @@ std::string word_wrap_text(const std::string& unwrapped_text, int font_size, current_word = *ch; ++ch; } else { - wchar_t previous = 0; + ucs4char previous = 0; for(;ch != utils::utf8_iterator::end(unwrapped_text) && *ch != ' ' && *ch != '\n'; ++ch) { diff --git a/src/marked-up_text.hpp b/src/marked-up_text.hpp index 6904b550b92..627fae9a356 100644 --- a/src/marked-up_text.hpp +++ b/src/marked-up_text.hpp @@ -22,6 +22,7 @@ class CVideo; struct surface; #include #include +#include "serialization/string_utils.hpp" namespace font { @@ -95,12 +96,12 @@ std::string del_tags(const std::string& text); bool is_format_char(char c); /** - * Determine if a wchar_t is a CJK character + * Determine if a ucs4char is a CJK character * * @retval true Input-char is a CJK char * @retval false Input-char is a not CJK char. */ -bool is_cjk_char(const wchar_t ch); +bool is_cjk_char(const ucs4char ch); /** Create string of color-markup, such as "<255,255,0>" for yellow. */ std::string color2markup(const SDL_Color &color); diff --git a/src/race.cpp b/src/race.cpp index 2d2fbbc6e2a..6d759ea29fd 100644 --- a/src/race.cpp +++ b/src/race.cpp @@ -44,12 +44,12 @@ static const config &empty_topics() { return cfg; } -static void add_prefixes(const wide_string& str, size_t length, markov_prefix_map& res) +static void add_prefixes(const ucs4_string& str, size_t length, markov_prefix_map& res) { for(size_t i = 0; i <= str.size(); ++i) { const size_t start = i > length ? i - length : 0; - const wide_string key(str.begin() + start, str.begin() + i); - const wchar_t c = i != str.size() ? str[i] : 0; + const ucs4_string key(str.begin() + start, str.begin() + i); + const ucs4char c = i != str.size() ? str[i] : 0; res[key].push_back(c); } } @@ -59,19 +59,19 @@ static markov_prefix_map markov_prefixes(const std::vector& items, markov_prefix_map res; for(std::vector::const_iterator i = items.begin(); i != items.end(); ++i) { - add_prefixes(utils::string_to_wstring(*i),length,res); + add_prefixes(utils::string_to_ucs4string(*i),length,res); } return res; } -static wide_string markov_generate_name(const markov_prefix_map& prefixes, +static ucs4_string markov_generate_name(const markov_prefix_map& prefixes, size_t chain_size, size_t max_len, rand_rng::simple_rng* rng) { if(chain_size == 0) - return wide_string(); + return ucs4_string(); - wide_string prefix, res; + ucs4_string prefix, res; // Since this function is called in the name description in a MP game it // uses the local locale. The locale between players can be different and @@ -96,7 +96,7 @@ static wide_string markov_generate_name(const markov_prefix_map& prefixes, return res; } - const wchar_t c = i->second[random[j++]%i->second.size()]; + const ucs4char c = i->second[random[j++]%i->second.size()]; if(c == 0) { return res; } @@ -119,17 +119,17 @@ static wide_string markov_generate_name(const markov_prefix_map& prefixes, // name has end-of-string as a possible next character in the // markov prefix map. If no valid ending is found, use the // originally generated name. - wide_string originalRes = res; + ucs4_string originalRes = res; int prefixLen; while(!res.empty()) { prefixLen = chain_size < res.size() ? chain_size : res.size(); - prefix = wide_string(res.end() - prefixLen, res.end()); + prefix = ucs4_string(res.end() - prefixLen, res.end()); const markov_prefix_map::const_iterator i = prefixes.find(prefix); if (i == prefixes.end() || i->second.empty()) { return res; } - if (std::find(i->second.begin(), i->second.end(), static_cast(0)) + if (std::find(i->second.begin(), i->second.end(), static_cast(0)) != i->second.end()) { // This ending is valid. return res; @@ -202,7 +202,7 @@ unit_race::unit_race(const config& cfg) : std::string unit_race::generate_name( unit_race::GENDER gender, rand_rng::simple_rng* rng) const { - return utils::wstring_to_string( + return utils::ucs4string_to_string( markov_generate_name(next_[gender], chain_size_, 12, rng)); } diff --git a/src/race.hpp b/src/race.hpp index 700e0affadf..7e5b90794c6 100644 --- a/src/race.hpp +++ b/src/race.hpp @@ -23,7 +23,7 @@ namespace rand_rng { } // namespace rand_rng -typedef std::map > markov_prefix_map; +typedef std::map markov_prefix_map; class unit_race { diff --git a/src/serialization/string_utils.cpp b/src/serialization/string_utils.cpp index ac50f9cb426..27607ad3555 100644 --- a/src/serialization/string_utils.cpp +++ b/src/serialization/string_utils.cpp @@ -25,7 +25,9 @@ #include "log.hpp" #include "serialization/string_utils.hpp" #include "util.hpp" +#include #include +#include #include static lg::log_domain log_engine("engine"); @@ -862,6 +864,24 @@ static int byte_size_from_utf8_first(unsigned char ch) return count; } +static size_t byte_size_from_ucs4_codepoint(ucs4char ch) +{ + if(ch < (1u << 7)) + return 1; + else if(ch < (1u << 11)) + return 2; + else if(ch < (1u << 16)) + return 3; + else if(ch < (1u << 21)) + return 4; + else if(ch < (1u << 26)) + return 5; + else if(ch < (1u << 31)) + return 6; + else + throw invalid_utf8_exception(); // Invalid UCS-4 +} + utf8_iterator::utf8_iterator(const std::string& str) : current_char(0), string_end(str.end()), @@ -901,7 +921,7 @@ utf8_iterator& utf8_iterator::operator++() return *this; } -wchar_t utf8_iterator::operator*() const +ucs4char utf8_iterator::operator*() const { return current_char; } @@ -946,35 +966,25 @@ void utf8_iterator::update() current_char = (current_char << 6) | (static_cast(*c) & 0x3F); } + + // Check for non-shortest-form encoding + // This has been forbidden in Unicode 3.1 for security reasons + if (size > byte_size_from_ucs4_codepoint(current_char)) + throw invalid_utf8_exception(); } -std::string wstring_to_string(const wide_string &src) +std::string ucs4string_to_string(const ucs4_string &src) { std::string ret; try { - for(wide_string::const_iterator i = src.begin(); i != src.end(); ++i) { + for(ucs4_string::const_iterator i = src.begin(); i != src.end(); ++i) { unsigned int count; - wchar_t ch = *i; + ucs4char ch = *i; // Determine the bytes required - count = 1; - if(ch >= 0x80) - count++; - - Uint32 bitmask = 0x800; - for(unsigned int j = 0; j < 5; ++j) { - if(static_cast(ch) >= bitmask) { - count++; - } - - bitmask <<= 5; - } - - if(count > 6) { - throw invalid_utf8_exception(); - } + count = byte_size_from_ucs4_codepoint(ch); if(count == 1) { ret.push_back(static_cast(ch)); @@ -994,21 +1004,21 @@ std::string wstring_to_string(const wide_string &src) return ret; } catch(invalid_utf8_exception&) { - ERR_GENERAL << "Invalid wide character string\n"; + ERR_GENERAL << "Invalid UCS-4 character string\n"; return ret; } } -std::string wchar_to_string(const wchar_t c) +std::string ucs4char_to_string(const ucs4char c) { - wide_string s; + ucs4_string s; s.push_back(c); - return wstring_to_string(s); + return ucs4string_to_string(s); } -wide_string string_to_wstring(const std::string &src) +ucs4_string string_to_ucs4string(const std::string &src) { - wide_string res; + ucs4_string res; try { utf8_iterator i1(src); @@ -1028,6 +1038,28 @@ wide_string string_to_wstring(const std::string &src) return res; } +utf16_string ucs4string_to_utf16string(const ucs4_string &src) +{ + utf16_string res; + const Uint32 bit17 = 0x10000; + BOOST_FOREACH(const ucs4char &u4, src) { + if(u4 < bit17) + res.push_back(static_cast(u4)); + else { + const Uint32 char20 = u4 - bit17; + assert(char20 < (1 << 20)); + const ucs4char lead = 0xD800 + (char20 >> 10); + const ucs4char trail = 0xDC00 + (char20 & 0x3FF); + assert(lead < bit17); + assert(trail < bit17); + res.push_back(static_cast(lead)); + res.push_back(static_cast(trail)); + } + } + + return res; +} + utf8_string lowercase(const utf8_string& s) { if(!s.empty()) { @@ -1035,15 +1067,12 @@ utf8_string lowercase(const utf8_string& s) std::string res; for(;itor != utf8_iterator::end(s); ++itor) { -#if defined(__APPLE__) || defined(__OpenBSD__) - /** @todo FIXME: Should we support towupper on recent OSX platforms? */ - wchar_t uchar = *itor; - if(uchar >= 0 && uchar < 0x100) - uchar = tolower(uchar); - res += utils::wchar_to_string(uchar); -#else - res += utils::wchar_to_string(towlower(*itor)); -#endif + ucs4char uchar = *itor; + // If wchar_t is less than 32 bits wide, we cannot apply towlower() to all codepoints + if(uchar <= static_cast(std::numeric_limits::max()) && + uchar >= static_cast(std::numeric_limits::min())) + uchar = towlower(static_cast(uchar)); + res += utils::ucs4char_to_string(uchar); } res.append(itor.substr().second, s.end()); @@ -1052,12 +1081,12 @@ utf8_string lowercase(const utf8_string& s) return s; } -void truncate_as_wstring(std::string& str, const size_t size) +void truncate_as_ucs4string(std::string& str, const size_t size) { - wide_string utf8_str = utils::string_to_wstring(str); + ucs4_string utf8_str = utils::string_to_ucs4string(str); if(utf8_str.size() > size) { utf8_str.resize(size); - str = utils::wstring_to_string(utf8_str); + str = utils::ucs4string_to_string(utf8_str); } } @@ -1065,7 +1094,7 @@ void ellipsis_truncate(std::string& str, const size_t size) { const size_t prev_size = str.length(); - truncate_as_wstring(str, size); + truncate_as_ucs4string(str, size); if(str.length() != prev_size) { str += ellipsis; diff --git a/src/serialization/string_utils.hpp b/src/serialization/string_utils.hpp index 494f1528c9a..8d837139160 100644 --- a/src/serialization/string_utils.hpp +++ b/src/serialization/string_utils.hpp @@ -26,10 +26,15 @@ #include "SDL_types.h" -/** The type we use to represent Unicode strings. */ -typedef std::vector wide_string; -typedef std::vector ucs4_string; +typedef Uint32 ucs4char; +typedef std::vector ucs4_string; typedef std::string utf8_string; +/** + * For win32 API. + * On windows, wchar_t is defined as Uint16 + * Wide strings are expected to be UTF-16 + */ +typedef std::vector utf16_string; class t_string; @@ -323,10 +328,10 @@ class utf8_iterator { public: typedef std::input_iterator_tag iterator_category; - typedef wchar_t value_type; + typedef ucs4char value_type; typedef ptrdiff_t difference_type; - typedef wchar_t* pointer; - typedef wchar_t& reference; + typedef ucs4char* pointer; + typedef ucs4char& reference; utf8_iterator(const std::string& str); utf8_iterator(std::string::const_iterator const &begin, std::string::const_iterator const &end); @@ -337,20 +342,22 @@ public: bool operator==(const utf8_iterator& a) const; bool operator!=(const utf8_iterator& a) const { return ! (*this == a); } utf8_iterator& operator++(); - wchar_t operator*() const; + ucs4char operator*() const; bool next_is_end(); const std::pair& substr() const; private: void update(); - wchar_t current_char; + ucs4char current_char; std::string::const_iterator string_end; std::pair current_substr; }; -std::string wstring_to_string(const wide_string &); -wide_string string_to_wstring(const std::string &); -std::string wchar_to_string(const wchar_t); +std::string ucs4string_to_string(const ucs4_string &); +ucs4_string string_to_ucs4string(const std::string &); +std::string ucs4char_to_string(const ucs4char); + +utf16_string ucs4string_to_utf16string(const ucs4_string &); /** Returns a lowercased version of the string. */ utf8_string lowercase(const utf8_string&); @@ -368,7 +375,7 @@ utf8_string lowercase(const utf8_string&); * characters. * @param size The size to truncate at. */ -void truncate_as_wstring(std::string& str, const size_t size); +void truncate_as_ucs4string(std::string& str, const size_t size); /** * Truncates a string to a given utf-8 character count and then appends an ellipsis. diff --git a/src/server/player_network.cpp b/src/server/player_network.cpp index 2a5543ecd49..c3a89dfaffb 100644 --- a/src/server/player_network.cpp +++ b/src/server/player_network.cpp @@ -29,12 +29,12 @@ const size_t max_message_length = 256; void truncate_message(const simple_wml::string_span& str, simple_wml::node& message) { // testing for msg.size() is not sufficient but we're not getting false negatives - // and it's cheaper than always converting to wstring. + // and it's cheaper than always converting to ucs4_string. if(str.size() > static_cast(chat_message::max_message_length)) { std::string tmp(str.begin(), str.end()); - // The string can contain utf-8 characters so truncate as wide_string otherwise + // The string can contain utf-8 characters so truncate as ucs4_string otherwise // a corrupted utf-8 string can be returned. - utils::truncate_as_wstring(tmp, max_message_length); + utils::truncate_as_ucs4string(tmp, max_message_length); message.set_attr_dup("message", tmp.c_str()); } } diff --git a/src/text.cpp b/src/text.cpp index 045b74fa195..f1101f16d40 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -185,15 +185,15 @@ unsigned ttext::insert_text(const unsigned offset, const std::string& text) return 0; } - return insert_unicode(offset, utils::string_to_wstring(text)); + return insert_unicode(offset, utils::string_to_ucs4string(text)); } -bool ttext::insert_unicode(const unsigned offset, const wchar_t unicode) +bool ttext::insert_unicode(const unsigned offset, ucs4char unicode) { - return (insert_unicode(offset, wide_string(1, unicode)) == 1); + return (insert_unicode(offset, ucs4_string(1, unicode)) == 1); } -unsigned ttext::insert_unicode(const unsigned offset, const wide_string& unicode) +unsigned ttext::insert_unicode(const unsigned offset, const ucs4_string& unicode) { assert(offset <= length_); @@ -204,10 +204,10 @@ unsigned ttext::insert_unicode(const unsigned offset, const wide_string& unicode const unsigned len = length_ + unicode.size() > maximum_length_ ? maximum_length_ - length_ : unicode.size(); - wide_string tmp = utils::string_to_wstring(text_); + ucs4_string tmp = utils::string_to_ucs4string(text_); tmp.insert(tmp.begin() + offset, unicode.begin(), unicode.begin() + len); - set_text(utils::wstring_to_string(tmp), false); + set_text(utils::ucs4string_to_string(tmp), false); return len; } @@ -295,8 +295,8 @@ bool ttext::set_text(const std::string& text, const bool markedup) if(markedup != markedup_text_ || text != text_) { assert(layout_); - const wide_string wide = utils::string_to_wstring(text); - const std::string narrow = utils::wstring_to_string(wide); + const ucs4_string wide = utils::string_to_ucs4string(text); + const std::string narrow = utils::ucs4string_to_string(wide); if(text != narrow) { ERR_GUI_L << "ttext::" << __func__ << " text '" << text @@ -450,9 +450,9 @@ ttext& ttext::set_maximum_length(const size_t maximum_length) maximum_length_ = maximum_length; if(length_ > maximum_length_) { - wide_string tmp = utils::string_to_wstring(text_); + ucs4_string tmp = utils::string_to_ucs4string(text_); tmp.resize(maximum_length_); - set_text(utils::wstring_to_string(tmp), false); + set_text(utils::ucs4string_to_string(tmp), false); } } diff --git a/src/text.hpp b/src/text.hpp index 43ea27287b7..23f62ce3431 100644 --- a/src/text.hpp +++ b/src/text.hpp @@ -16,6 +16,7 @@ #define TEXT_HPP_INCLUDED #include "sdl_utils.hpp" +#include "serialization/string_utils.hpp" #include @@ -101,7 +102,7 @@ public: * * @returns True upon success, false otherwise. */ - bool insert_unicode(const unsigned offset, const wchar_t unicode); + bool insert_unicode(const unsigned offset, ucs4char unicode); /** * Inserts unicode text. @@ -112,7 +113,7 @@ public: * @returns The number of characters inserted. */ unsigned insert_unicode( - const unsigned offset, const std::vector& unicode); + const unsigned offset, const ucs4_string& unicode); /***** ***** ***** ***** Font flags ***** ***** ***** *****/ diff --git a/src/widgets/textbox.cpp b/src/widgets/textbox.cpp index 28b3d4b5acd..14c97f25a52 100644 --- a/src/widgets/textbox.cpp +++ b/src/widgets/textbox.cpp @@ -29,7 +29,7 @@ static lg::log_domain log_display("display"); namespace gui { textbox::textbox(CVideo &video, int width, const std::string& text, bool editable, size_t max_size, int font_size, double alpha, double alpha_focus, const bool auto_join) - : scrollarea(video, auto_join), max_size_(max_size), font_size_(font_size), text_(utils::string_to_wstring(text)), + : scrollarea(video, auto_join), max_size_(max_size), font_size_(font_size), text_(utils::string_to_ucs4string(text)), cursor_(text_.size()), selstart_(-1), selend_(-1), grabmouse_(false), text_pos_(0), editable_(editable), show_cursor_(true), show_cursor_at_(0), text_image_(NULL), @@ -65,14 +65,14 @@ void textbox::set_inner_location(SDL_Rect const &rect) const std::string textbox::text() const { - const std::string &ret = utils::wstring_to_string(text_); + const std::string &ret = utils::ucs4string_to_string(text_); return ret; } // set_text does not respect max_size_ void textbox::set_text(const std::string& text, const SDL_Color& color) { - text_ = utils::string_to_wstring(text); + text_ = utils::string_to_ucs4string(text); cursor_ = text_.size(); text_pos_ = 0; selstart_ = -1; @@ -94,7 +94,7 @@ void textbox::append_text(const std::string& text, bool auto_scroll, const SDL_C return; } const bool is_at_bottom = get_position() == get_max_position(); - const wide_string& wtext = utils::string_to_wstring(text); + const ucs4_string& wtext = utils::string_to_ucs4string(text); const surface new_text = add_text_line(wtext, color); surface new_surface = create_compatible_surface(text_image_,std::max(text_image_->w,new_text->w),text_image_->h+new_text->h); @@ -286,7 +286,7 @@ void textbox::scroll(unsigned int pos) set_dirty(true); } -surface textbox::add_text_line(const wide_string& text, const SDL_Color& color) +surface textbox::add_text_line(const ucs4_string& text, const SDL_Color& color) { line_height_ = font::get_max_height(font_size_); @@ -303,17 +303,17 @@ surface textbox::add_text_line(const wide_string& text, const SDL_Color& color) // some more complex scripts (that is, RTL languages). This part of the work should // actually be done by the font-rendering system. std::string visible_string; - wide_string wrapped_text; + ucs4_string wrapped_text; - wide_string::const_iterator backup_itor = text.end(); + ucs4_string::const_iterator backup_itor = text.end(); - wide_string::const_iterator itor = text.begin(); + ucs4_string::const_iterator itor = text.begin(); while(itor != text.end()) { //If this is a space, save copies of the current state so we can roll back if(char(*itor) == ' ') { backup_itor = itor; } - visible_string.append(utils::wchar_to_string(*itor)); + visible_string.append(utils::ucs4char_to_string(*itor)); if(char(*itor) == '\n') { backup_itor = text.end(); @@ -333,7 +333,7 @@ surface textbox::add_text_line(const wide_string& text, const SDL_Color& color) } } backup_itor = text.end(); - wrapped_text.push_back(wchar_t('\n')); + wrapped_text.push_back(ucs4char('\n')); char_x_.push_back(0); char_y_.push_back(char_y_.back() + line_height_); visible_string = ""; @@ -345,7 +345,7 @@ surface textbox::add_text_line(const wide_string& text, const SDL_Color& color) } } - const std::string s = utils::wstring_to_string(wrapped_text); + const std::string s = utils::ucs4string_to_string(wrapped_text); const surface res(font::get_rendered_text(s, font_size_, color)); return res; @@ -386,7 +386,7 @@ void textbox::erase_selection() if(!is_selection()) return; - wide_string::iterator itor = text_.begin() + std::min(selstart_, selend_); + ucs4_string::iterator itor = text_.begin() + std::min(selstart_, selend_); text_.erase(itor, itor + abs(selend_ - selstart_)); cursor_ = std::min(selstart_, selend_); selstart_ = selend_ = -1; @@ -580,9 +580,9 @@ void textbox::handle_event(const SDL_Event& event, bool was_forwarded) } #if SDL_VERSION_ATLEAST(2, 0, 0) - wchar_t character = key.scancode; + ucs4char character = key.scancode; #else - wchar_t character = key.unicode; + ucs4char character = key.unicode; #endif //movement characters may have a "Unicode" field on some platforms, so ignore it. @@ -615,7 +615,7 @@ void textbox::handle_event(const SDL_Event& event, bool was_forwarded) //cut off anything after the first newline str.erase(std::find_if(str.begin(),str.end(),utils::isnewline),str.end()); - wide_string s = utils::string_to_wstring(str); + ucs4_string s = utils::string_to_ucs4string(str); if(text_.size() < max_size_) { if(s.size() + text_.size() > max_size_) { @@ -636,8 +636,8 @@ void textbox::handle_event(const SDL_Event& event, bool was_forwarded) const size_t beg = std::min(size_t(selstart_),size_t(selend_)); const size_t end = std::max(size_t(selstart_),size_t(selend_)); - wide_string ws = wide_string(text_.begin() + beg, text_.begin() + end); - std::string s = utils::wstring_to_string(ws); + ucs4_string ws(text_.begin() + beg, text_.begin() + end); + std::string s = utils::ucs4string_to_string(ws); copy_to_clipboard(s, false); } } diff --git a/src/widgets/textbox.hpp b/src/widgets/textbox.hpp index ad998a013a4..2e73084c2f7 100644 --- a/src/widgets/textbox.hpp +++ b/src/widgets/textbox.hpp @@ -55,13 +55,13 @@ protected: virtual void scroll(unsigned int pos); private: - virtual void handle_text_changed(const wide_string&) {} + virtual void handle_text_changed(const ucs4_string&) {} size_t max_size_; int font_size_; - wide_string text_; + ucs4_string text_; // mutable unsigned int firstOnScreen_; int cursor_; @@ -100,7 +100,7 @@ private: void draw_cursor(int pos, CVideo &video) const; void update_text_cache(bool reset = false, const SDL_Color& color =font::NORMAL_COLOR); - surface add_text_line(const wide_string& text, const SDL_Color& color =font::NORMAL_COLOR); + surface add_text_line(const ucs4_string& text, const SDL_Color& color =font::NORMAL_COLOR); bool is_selection(); void erase_selection(); diff --git a/src/windows_tray_notification.cpp b/src/windows_tray_notification.cpp index 39bd37b31e6..03fd5a213db 100644 --- a/src/windows_tray_notification.cpp +++ b/src/windows_tray_notification.cpp @@ -1,210 +1,211 @@ -/* - Copyright (C) 2013 - 2014 by Maxim Biro - Part of the Battle for Wesnoth Project http://www.wesnoth.org/ - - 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. - - See the COPYING file for more details. -*/ - -#include "windows_tray_notification.hpp" - -#include - -#include "gettext.hpp" -#include "serialization/string_utils.hpp" - -NOTIFYICONDATA* windows_tray_notification::nid = NULL; -bool windows_tray_notification::message_reset = false; - -void windows_tray_notification::destroy_tray_icon() -{ - if (nid == NULL) { - return; - } - - if (!message_reset){ - Shell_NotifyIcon(NIM_DELETE, nid); - delete nid; - nid = NULL; - } else { - message_reset = false; - } -} - -void windows_tray_notification::handle_system_event(const SDL_Event& event) -{ - if (event.syswm.msg->msg != WM_TRAYNOTIFY) { - return; - } - - if (event.syswm.msg->lParam == NIN_BALLOONUSERCLICK) { - switch_to_wesnoth_window(); - destroy_tray_icon(); - } else if (event.syswm.msg->lParam == NIN_BALLOONTIMEOUT) { - destroy_tray_icon(); - } -} - -bool windows_tray_notification::create_tray_icon() -{ - // getting handle to a 32x32 icon, contained in "WESNOTH_ICON" icon group of wesnoth.exe resources - const HMODULE wesnoth_exe = GetModuleHandle(NULL); - if (wesnoth_exe == NULL) { - return false; - } - - const HRSRC group_icon_info = FindResource(wesnoth_exe, L"WESNOTH_ICON", RT_GROUP_ICON); - if (group_icon_info == NULL) { - return false; - } - - HGLOBAL hGlobal = LoadResource(wesnoth_exe, group_icon_info); - if (hGlobal == NULL) { - return false; - } - - const PBYTE group_icon_res = static_cast(LockResource(hGlobal)); - if (group_icon_res == NULL) { - return false; - } - - const int nID = LookupIconIdFromDirectoryEx(group_icon_res, TRUE, 32, 32, LR_DEFAULTCOLOR); - if (nID == 0) { - return false; - } - - const HRSRC icon_info = FindResource(wesnoth_exe, MAKEINTRESOURCE(nID), MAKEINTRESOURCE(3)); - if (icon_info == NULL) { - return false; - } - - hGlobal = LoadResource(wesnoth_exe, icon_info); - if (hGlobal == NULL) { - return false; - } - - const PBYTE icon_res = static_cast(LockResource(hGlobal)); - if (icon_res == NULL) { - return false; - } - - const HICON icon = CreateIconFromResource(icon_res, SizeofResource(wesnoth_exe, icon_info), TRUE, 0x00030000); - if (icon == NULL) { - return false; - } - - const HWND window = get_window_handle(); - if (window == NULL) { - return false; - } - - const std::wstring& wtip = string_to_wstring(_("The Battle for Wesnoth")); - - // filling notification structure - nid = new NOTIFYICONDATA; - memset(nid, 0, sizeof(&nid)); - nid->cbSize = NOTIFYICONDATA_V2_SIZE; - nid->hWnd = window; - nid->uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE; - nid->dwInfoFlags = NIIF_USER; - nid->uVersion = NOTIFYICON_VERSION; - nid->uCallbackMessage = WM_TRAYNOTIFY; - nid->uID = ICON_ID; - nid->hIcon = icon; -#if _WIN32_WINNT >= 0x600 - nid->hBalloonIcon = icon; -#endif - lstrcpy(nid->szTip, wtip.c_str()); - - // creating icon notification - return Shell_NotifyIcon(NIM_ADD, nid) != FALSE; -} - -bool windows_tray_notification::set_tray_message(const std::string& title, const std::string& message) -{ - // prevents deletion of icon when resetting already existing notification - message_reset = (nid->uFlags & NIF_INFO) != 0; - - nid->uFlags |= NIF_INFO; - lstrcpy(nid->szInfoTitle, string_to_wstring(title).c_str()); - lstrcpy(nid->szInfo, string_to_wstring(message).c_str()); - - // setting notification - return Shell_NotifyIcon(NIM_MODIFY, nid) != FALSE; -} - -void windows_tray_notification::adjust_length(std::string& title, std::string& message) -{ - static const int ELIPSIS_LENGTH = 3; - - // limitations set by winapi - if (title.length() > MAX_TITLE_LENGTH) { - utils::ellipsis_truncate(title, MAX_TITLE_LENGTH - ELIPSIS_LENGTH); - } - if (message.length() > MAX_MESSAGE_LENGTH) { - utils::ellipsis_truncate(message, MAX_MESSAGE_LENGTH - ELIPSIS_LENGTH); - } -} - -HWND windows_tray_notification::get_window_handle() -{ - SDL_SysWMinfo wmInfo; - SDL_VERSION(&wmInfo.version); - if (SDL_GetWMInfo(&wmInfo) != 1) { - return NULL; - } - - return wmInfo.window; -} - -void windows_tray_notification::switch_to_wesnoth_window() -{ - const HWND window = get_window_handle(); - if (window == NULL) { - return; - } - - if (IsIconic(window)) { - ShowWindow(window, SW_RESTORE); - } - SetForegroundWindow(window); -} - -std::wstring windows_tray_notification::string_to_wstring(const std::string& string) -{ - const std::vector wide_string = utils::string_to_wstring(string); - return std::wstring(wide_string.begin(), wide_string.end()); -} - -bool windows_tray_notification::show(std::string title, std::string message) -{ - adjust_length(title, message); - - const bool tray_icon_exist = nid != NULL; - if (!tray_icon_exist) { - const bool tray_icon_created = create_tray_icon(); - if (!tray_icon_created) { - const bool memory_allocated = nid != NULL; - if (memory_allocated) { - destroy_tray_icon(); - } - return false; - } - } - - // at this point tray icon was just created or already existed before, so it's safe to call `set_tray_message` - - const bool result = set_tray_message(title, message); - // the `destroy_tray_icon` will be called by event only if `set_tray_message` succeeded - // if it doesn't succeed, we have to call `destroy_tray_icon` manually - if (!result) { - destroy_tray_icon(); - } - return result; -} +/* + Copyright (C) 2013 - 2014 by Maxim Biro + Part of the Battle for Wesnoth Project http://www.wesnoth.org/ + + 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. + + See the COPYING file for more details. +*/ + +#include "windows_tray_notification.hpp" + +#include + +#include "gettext.hpp" +#include "serialization/string_utils.hpp" + +NOTIFYICONDATA* windows_tray_notification::nid = NULL; +bool windows_tray_notification::message_reset = false; + +void windows_tray_notification::destroy_tray_icon() +{ + if (nid == NULL) { + return; + } + + if (!message_reset){ + Shell_NotifyIcon(NIM_DELETE, nid); + delete nid; + nid = NULL; + } else { + message_reset = false; + } +} + +void windows_tray_notification::handle_system_event(const SDL_Event& event) +{ + if (event.syswm.msg->msg != WM_TRAYNOTIFY) { + return; + } + + if (event.syswm.msg->lParam == NIN_BALLOONUSERCLICK) { + switch_to_wesnoth_window(); + destroy_tray_icon(); + } else if (event.syswm.msg->lParam == NIN_BALLOONTIMEOUT) { + destroy_tray_icon(); + } +} + +bool windows_tray_notification::create_tray_icon() +{ + // getting handle to a 32x32 icon, contained in "WESNOTH_ICON" icon group of wesnoth.exe resources + const HMODULE wesnoth_exe = GetModuleHandle(NULL); + if (wesnoth_exe == NULL) { + return false; + } + + const HRSRC group_icon_info = FindResource(wesnoth_exe, L"WESNOTH_ICON", RT_GROUP_ICON); + if (group_icon_info == NULL) { + return false; + } + + HGLOBAL hGlobal = LoadResource(wesnoth_exe, group_icon_info); + if (hGlobal == NULL) { + return false; + } + + const PBYTE group_icon_res = static_cast(LockResource(hGlobal)); + if (group_icon_res == NULL) { + return false; + } + + const int nID = LookupIconIdFromDirectoryEx(group_icon_res, TRUE, 32, 32, LR_DEFAULTCOLOR); + if (nID == 0) { + return false; + } + + const HRSRC icon_info = FindResource(wesnoth_exe, MAKEINTRESOURCE(nID), MAKEINTRESOURCE(3)); + if (icon_info == NULL) { + return false; + } + + hGlobal = LoadResource(wesnoth_exe, icon_info); + if (hGlobal == NULL) { + return false; + } + + const PBYTE icon_res = static_cast(LockResource(hGlobal)); + if (icon_res == NULL) { + return false; + } + + const HICON icon = CreateIconFromResource(icon_res, SizeofResource(wesnoth_exe, icon_info), TRUE, 0x00030000); + if (icon == NULL) { + return false; + } + + const HWND window = get_window_handle(); + if (window == NULL) { + return false; + } + + const std::wstring& wtip = string_to_wstring(_("The Battle for Wesnoth")); + + // filling notification structure + nid = new NOTIFYICONDATA; + memset(nid, 0, sizeof(&nid)); + nid->cbSize = NOTIFYICONDATA_V2_SIZE; + nid->hWnd = window; + nid->uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE; + nid->dwInfoFlags = NIIF_USER; + nid->uVersion = NOTIFYICON_VERSION; + nid->uCallbackMessage = WM_TRAYNOTIFY; + nid->uID = ICON_ID; + nid->hIcon = icon; +#if _WIN32_WINNT >= 0x600 + nid->hBalloonIcon = icon; +#endif + lstrcpy(nid->szTip, wtip.c_str()); + + // creating icon notification + return Shell_NotifyIcon(NIM_ADD, nid) != FALSE; +} + +bool windows_tray_notification::set_tray_message(const std::string& title, const std::string& message) +{ + // prevents deletion of icon when resetting already existing notification + message_reset = (nid->uFlags & NIF_INFO) != 0; + + nid->uFlags |= NIF_INFO; + lstrcpy(nid->szInfoTitle, string_to_wstring(title).c_str()); + lstrcpy(nid->szInfo, string_to_wstring(message).c_str()); + + // setting notification + return Shell_NotifyIcon(NIM_MODIFY, nid) != FALSE; +} + +void windows_tray_notification::adjust_length(std::string& title, std::string& message) +{ + static const int ELIPSIS_LENGTH = 3; + + // limitations set by winapi + if (title.length() > MAX_TITLE_LENGTH) { + utils::ellipsis_truncate(title, MAX_TITLE_LENGTH - ELIPSIS_LENGTH); + } + if (message.length() > MAX_MESSAGE_LENGTH) { + utils::ellipsis_truncate(message, MAX_MESSAGE_LENGTH - ELIPSIS_LENGTH); + } +} + +HWND windows_tray_notification::get_window_handle() +{ + SDL_SysWMinfo wmInfo; + SDL_VERSION(&wmInfo.version); + if (SDL_GetWMInfo(&wmInfo) != 1) { + return NULL; + } + + return wmInfo.window; +} + +void windows_tray_notification::switch_to_wesnoth_window() +{ + const HWND window = get_window_handle(); + if (window == NULL) { + return; + } + + if (IsIconic(window)) { + ShowWindow(window, SW_RESTORE); + } + SetForegroundWindow(window); +} + +std::wstring windows_tray_notification::string_to_wstring(const std::string& string) +{ + const utils::ucs4_string u4_string = utils::string_to_ucs4string(string); + const utils::utf16_string u16_string = utils::ucs4string_to_utf16string(u4_string); + return std::wstring(utf16_string.begin(), utf16_string.end()); +} + +bool windows_tray_notification::show(std::string title, std::string message) +{ + adjust_length(title, message); + + const bool tray_icon_exist = nid != NULL; + if (!tray_icon_exist) { + const bool tray_icon_created = create_tray_icon(); + if (!tray_icon_created) { + const bool memory_allocated = nid != NULL; + if (memory_allocated) { + destroy_tray_icon(); + } + return false; + } + } + + // at this point tray icon was just created or already existed before, so it's safe to call `set_tray_message` + + const bool result = set_tray_message(title, message); + // the `destroy_tray_icon` will be called by event only if `set_tray_message` succeeded + // if it doesn't succeed, we have to call `destroy_tray_icon` manually + if (!result) { + destroy_tray_icon(); + } + return result; +} diff --git a/src/windows_tray_notification.hpp b/src/windows_tray_notification.hpp index 2279e6ddf74..f5ac5b051ca 100644 --- a/src/windows_tray_notification.hpp +++ b/src/windows_tray_notification.hpp @@ -1,83 +1,83 @@ -/* - Copyright (C) 2013 - 2014 by Maxim Biro - Part of the Battle for Wesnoth Project http://www.wesnoth.org/ - - 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. - - See the COPYING file for more details. -*/ - -#ifndef WINDOWS_TRAY_NOTIFICATION_HPP_INCLUDED -#define WINDOWS_TRAY_NOTIFICATION_HPP_INCLUDED - -#include -#include -//forces to call Unicode winapi functions instead of ASCII (default) -#define UNICODE -//defines that mingw misses -#ifndef _WIN32_IE - #define _WIN32_IE 0x0600 //specifying target platform to be Windows XP and higher -#endif -#ifndef NIIF_USER - #define NIIF_USER 0x00000004 -#endif -#ifndef NIN_BALLOONTIMEOUT - #define NIN_BALLOONTIMEOUT (WM_USER + 4) -#endif -#ifndef NIN_BALLOONUSERCLICK - #define NIN_BALLOONUSERCLICK (WM_USER + 5) -#endif -// ShellAPI.h should be included after Windows.h only! -#include -#include - -class windows_tray_notification { -public: - /** - * Displays a tray notification. - * When user clicks on the notification popup, the user switches to the wesnoth window. - * - * @param title Title of a notification. Gets truncated if longer than 64 characters, including - * the terminating null character. - * @param message Message of a notification. Gets truncated if longer than 256 characters, including - * the terminating null character. - * - * @return True if message was shown successfully, False otherwise. - */ - static bool show(std::string title, std::string message); - - /** - * Frees resources when a notification disappears, switches user to the wesnoth - * window if the notification popup was clicked by user. - * - * @param event System event. - */ - static void handle_system_event(const SDL_Event& event); - -private: - static NOTIFYICONDATA* nid; - static bool message_reset; - static const int ICON_ID = 1007; // just a random number - static const unsigned int WM_TRAYNOTIFY = 32868; // WM_APP+100 - static const size_t MAX_TITLE_LENGTH = 63; // 64 including the terminating null character - static const size_t MAX_MESSAGE_LENGTH = 255; // 256 including the terminating null character - - static bool create_tray_icon(); - static void destroy_tray_icon(); - static bool set_tray_message(const std::string& title, const std::string& message); - static void adjust_length(std::string& title, std::string& message); - static HWND get_window_handle(); - static void switch_to_wesnoth_window(); - static std::wstring string_to_wstring(const std::string& string); - - explicit windows_tray_notification(); - windows_tray_notification(const windows_tray_notification& w); - windows_tray_notification& operator=(const windows_tray_notification& w); -}; - -#endif // WINDOWS_TRAY_NOTIFICATION_HPP_INCLUDED +/* + Copyright (C) 2013 - 2014 by Maxim Biro + Part of the Battle for Wesnoth Project http://www.wesnoth.org/ + + 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. + + See the COPYING file for more details. +*/ + +#ifndef WINDOWS_TRAY_NOTIFICATION_HPP_INCLUDED +#define WINDOWS_TRAY_NOTIFICATION_HPP_INCLUDED + +#include +#include +//forces to call Unicode winapi functions instead of ASCII (default) +#define UNICODE +//defines that mingw misses +#ifndef _WIN32_IE + #define _WIN32_IE 0x0600 //specifying target platform to be Windows XP and higher +#endif +#ifndef NIIF_USER + #define NIIF_USER 0x00000004 +#endif +#ifndef NIN_BALLOONTIMEOUT + #define NIN_BALLOONTIMEOUT (WM_USER + 4) +#endif +#ifndef NIN_BALLOONUSERCLICK + #define NIN_BALLOONUSERCLICK (WM_USER + 5) +#endif +// ShellAPI.h should be included after Windows.h only! +#include +#include + +class windows_tray_notification { +public: + /** + * Displays a tray notification. + * When user clicks on the notification popup, the user switches to the wesnoth window. + * + * @param title Title of a notification. Gets truncated if longer than 64 characters, including + * the terminating null character. + * @param message Message of a notification. Gets truncated if longer than 256 characters, including + * the terminating null character. + * + * @return True if message was shown successfully, False otherwise. + */ + static bool show(std::string title, std::string message); + + /** + * Frees resources when a notification disappears, switches user to the wesnoth + * window if the notification popup was clicked by user. + * + * @param event System event. + */ + static void handle_system_event(const SDL_Event& event); + +private: + static NOTIFYICONDATA* nid; + static bool message_reset; + static const int ICON_ID = 1007; // just a random number + static const unsigned int WM_TRAYNOTIFY = 32868; // WM_APP+100 + static const size_t MAX_TITLE_LENGTH = 63; // 64 including the terminating null character + static const size_t MAX_MESSAGE_LENGTH = 255; // 256 including the terminating null character + + static bool create_tray_icon(); + static void destroy_tray_icon(); + static bool set_tray_message(const std::string& title, const std::string& message); + static void adjust_length(std::string& title, std::string& message); + static HWND get_window_handle(); + static void switch_to_wesnoth_window(); + static std::wstring string_to_wstring(const std::string& string); + + explicit windows_tray_notification(); + windows_tray_notification(const windows_tray_notification& w); + windows_tray_notification& operator=(const windows_tray_notification& w); +}; + +#endif // WINDOWS_TRAY_NOTIFICATION_HPP_INCLUDED