Merge branch 'wide_string_elimination'

Conflicts:
	src/dialogs.cpp
	src/gui/dialogs/addon_list.cpp
	src/gui/widgets/password_box.cpp
	src/gui/widgets/text.cpp
	src/gui/widgets/text_box.cpp
	src/marked-up_text.cpp
	src/serialization/string_utils.cpp
	src/serialization/string_utils.hpp
	src/text.cpp
This commit is contained in:
Alexander van Gessel 2014-03-23 03:46:01 +01:00
commit b0d31d07e3
20 changed files with 490 additions and 421 deletions

View File

@ -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 // Versions are too important in upgrades mode, so don't
// truncate them then. // truncate them then.
if(!updates_only) { 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) { 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) { if(state == ADDON_INSTALLED_UPGRADABLE) {
display_version = display_version =

View File

@ -910,8 +910,8 @@ void filter_textbox::delete_item(int selection) {
/* dialog_.set_menu_items(filtered_items_); */ /* dialog_.set_menu_items(filtered_items_); */
} }
void filter_textbox::handle_text_changed(const wide_string& text) { void filter_textbox::handle_text_changed(const ucs4_string& text) {
const std::vector<std::string> words = utils::split(utils::wstring_to_string(text),' '); const std::vector<std::string> words = utils::split(utils::ucs4string_to_string(text),' ');
if (words == last_words) if (words == last_words)
return; return;
last_words = words; last_words = words;

View File

@ -145,7 +145,7 @@ private:
std::vector<std::string> last_words; std::vector<std::string> last_words;
size_t header_row_; size_t header_row_;
gui::dialog& dialog_; 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 { class dialog_button : public button {

View File

@ -91,10 +91,11 @@ bool open_object(const std::string& path_or_url)
LOG_DU << "open_object(): on Win32, will use ShellExecute()\n"; LOG_DU << "open_object(): on Win32, will use ShellExecute()\n";
wide_string wpath = utils::string_to_wstring(path_or_url); ucs4_string u4path = utils::string_to_ucs4string(path_or_url);
wpath.push_back(wchar_t(0)); // Make wpath NULL-terminated utf16_string u16path = utils::ucs4string_to_utf16string(u4path);
u16path.push_back(wchar_t(0)); // Make wpath NULL-terminated
const ptrdiff_t res = reinterpret_cast<ptrdiff_t>(ShellExecute(NULL, L"open", &wpath.front(), NULL, NULL, SW_SHOW)); const ptrdiff_t res = reinterpret_cast<ptrdiff_t>(ShellExecute(NULL, L"open", &u16path.front(), NULL, NULL, SW_SHOW));
if(res <= 32) { if(res <= 32) {
ERR_DU << "open_object(): ShellExecute() failed (" << res << ")\n"; ERR_DU << "open_object(): ShellExecute() failed (" << res << ")\n";
return false; return false;

View File

@ -3345,9 +3345,9 @@ std::string get_first_word(const std::string &s)
if (ch == utf8::iterator::end(re)) if (ch == utf8::iterator::end(re))
return re; return re;
wchar_t firstchar = *ch; ucs4char firstchar = *ch;
if (font::is_cjk_char(firstchar)) { if (font::is_cjk_char(firstchar)) {
re = utils::wchar_to_string(firstchar); re = utils::ucs4char_to_string(firstchar);
} }
return re; return re;
} }

View File

@ -360,7 +360,7 @@ void hotkey_item::load_from_config(const config& cfg)
return; 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: // They may really want a specific key on the keyboard:
// we assume that any single character keyname is a character. // 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_hat() >= 0) item["hat"] = get_hat();
if (get_value() >= 0) item["value"] = get_value(); if (get_value() >= 0) item["value"] = get_value();
if (get_keycode() >= 0) item["key"] = SDL_GetKeyName(SDLKey(get_keycode())); 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_mouse() >= 0) item["mouse"] = get_mouse();
if (get_button() >= 0) item["button"] = get_button(); if (get_button() >= 0) item["button"] = get_button();

View File

@ -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/ * 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. * 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<unsigned int>(c);
//FIXME add range from Japanese-specific and Korean-specific section if you know the characters are used today. //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 if (ch < 0x2e80) return false; // shortcut for common non-CJK
@ -359,7 +356,7 @@ namespace {
* CJK (CJK punctuations) * CJK (CJK punctuations)
* http://www.unicode.org/charts/PDF/U3000.pdf * 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 return
/** /**
@ -380,7 +377,7 @@ inline bool no_break_after(const wchar_t ch)
ch == 0x3016 || ch == 0x301a || ch == 0x301d; ch == 0x3016 || ch == 0x301a || ch == 0x301d;
} }
inline bool no_break_before(const wchar_t ch) inline bool no_break_before(const ucs4char ch)
{ {
return return
/** /**
@ -417,7 +414,7 @@ inline bool no_break_before(const wchar_t ch)
ch == 0x301b || ch == 0x301e; ch == 0x301b || ch == 0x301e;
} }
inline bool break_before(const wchar_t ch) inline bool break_before(const ucs4char ch)
{ {
if(no_break_before(ch)) if(no_break_before(ch))
return false; return false;
@ -425,7 +422,7 @@ inline bool break_before(const wchar_t ch)
return is_cjk_char(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)) if(no_break_after(ch))
return false; return false;
@ -459,7 +456,7 @@ std::string word_wrap_text(const std::string& unwrapped_text, int font_size,
if(start_of_line) { if(start_of_line) {
line_width = 0; line_width = 0;
format_string.clear(); format_string.clear();
while(ch != end && *ch < static_cast<wchar_t>(0x100) while(ch != end && *ch < static_cast<ucs4char>(0x100)
&& is_format_char(*ch) && !ch.next_is_end()) { && is_format_char(*ch) && !ch.next_is_end()) {
format_string.append(ch.substr().first, ch.substr().second); 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; current_word = *ch;
++ch; ++ch;
} else { } else {
wchar_t previous = 0; ucs4char previous = 0;
for(;ch != utf8::iterator::end(unwrapped_text) && for(;ch != utf8::iterator::end(unwrapped_text) &&
*ch != ' ' && *ch != '\n'; ++ch) { *ch != ' ' && *ch != '\n'; ++ch) {

View File

@ -22,6 +22,7 @@ class CVideo;
struct surface; struct surface;
#include <SDL_video.h> #include <SDL_video.h>
#include <string> #include <string>
#include "serialization/string_utils.hpp"
namespace font { namespace font {
@ -95,12 +96,12 @@ std::string del_tags(const std::string& text);
bool is_format_char(char c); 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 true Input-char is a CJK char
* @retval false Input-char is a not 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. */ /** Create string of color-markup, such as "<255,255,0>" for yellow. */
std::string color2markup(const SDL_Color &color); std::string color2markup(const SDL_Color &color);

View File

@ -44,12 +44,12 @@ static const config &empty_topics() {
return cfg; 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) { for(size_t i = 0; i <= str.size(); ++i) {
const size_t start = i > length ? i - length : 0; const size_t start = i > length ? i - length : 0;
const wide_string key(str.begin() + start, str.begin() + i); const ucs4_string key(str.begin() + start, str.begin() + i);
const wchar_t c = i != str.size() ? str[i] : 0; const ucs4char c = i != str.size() ? str[i] : 0;
res[key].push_back(c); res[key].push_back(c);
} }
} }
@ -59,19 +59,19 @@ static markov_prefix_map markov_prefixes(const std::vector<std::string>& items,
markov_prefix_map res; markov_prefix_map res;
for(std::vector<std::string>::const_iterator i = items.begin(); i != items.end(); ++i) { for(std::vector<std::string>::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; 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) size_t chain_size, size_t max_len, rand_rng::simple_rng* rng)
{ {
if(chain_size == 0) 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 // 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 // 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; 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) { if(c == 0) {
return res; 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 // name has end-of-string as a possible next character in the
// markov prefix map. If no valid ending is found, use the // markov prefix map. If no valid ending is found, use the
// originally generated name. // originally generated name.
wide_string originalRes = res; ucs4_string originalRes = res;
int prefixLen; int prefixLen;
while(!res.empty()) { while(!res.empty()) {
prefixLen = chain_size < res.size() ? chain_size : res.size(); 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); const markov_prefix_map::const_iterator i = prefixes.find(prefix);
if (i == prefixes.end() || i->second.empty()) { if (i == prefixes.end() || i->second.empty()) {
return res; return res;
} }
if (std::find(i->second.begin(), i->second.end(), static_cast<wchar_t>(0)) if (std::find(i->second.begin(), i->second.end(), static_cast<ucs4char>(0))
!= i->second.end()) { != i->second.end()) {
// This ending is valid. // This ending is valid.
return res; return res;
@ -202,7 +202,7 @@ unit_race::unit_race(const config& cfg) :
std::string unit_race::generate_name( std::string unit_race::generate_name(
unit_race::GENDER gender, rand_rng::simple_rng* rng) const 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)); markov_generate_name(next_[gender], chain_size_, 12, rng));
} }

View File

@ -23,7 +23,7 @@ namespace rand_rng {
} // namespace rand_rng } // namespace rand_rng
typedef std::map<wide_string, std::vector<wchar_t> > markov_prefix_map; typedef std::map<ucs4_string, ucs4_string > markov_prefix_map;
class unit_race class unit_race
{ {

View File

@ -25,13 +25,35 @@
#include "log.hpp" #include "log.hpp"
#include "serialization/string_utils.hpp" #include "serialization/string_utils.hpp"
#include "util.hpp" #include "util.hpp"
#include <limits>
#include <boost/array.hpp> #include <boost/array.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
static lg::log_domain log_engine("engine"); static lg::log_domain log_engine("engine");
#define ERR_GENERAL LOG_STREAM(err, lg::general) #define ERR_GENERAL LOG_STREAM(err, lg::general)
#define ERR_NG LOG_STREAM(err, log_engine) #define ERR_NG LOG_STREAM(err, log_engine)
namespace {
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 utf8::invalid_utf8_exception(); // Invalid UCS-4
}
} // anonymous namespace
namespace utils { namespace utils {
const std::string ellipsis = "..."; const std::string ellipsis = "...";
@ -840,33 +862,17 @@ std::vector< std::pair< int, int > > parse_ranges(std::string const &str)
return to_return; return to_return;
} }
std::string ucs4string_to_string(const ucs4_string &src)
std::string wstring_to_string(const wide_string &src)
{ {
std::string ret; std::string ret;
try { 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; unsigned int count;
wchar_t ch = *i; ucs4char ch = *i;
// Determine the bytes required // Determine the bytes required
count = 1; count = byte_size_from_ucs4_codepoint(ch);
if(ch >= 0x80)
count++;
Uint32 bitmask = 0x800;
for(unsigned int j = 0; j < 5; ++j) {
if(static_cast<Uint32>(ch) >= bitmask) {
count++;
}
bitmask <<= 5;
}
if(count > 6) {
throw utf8::invalid_utf8_exception();
}
if(count == 1) { if(count == 1) {
ret.push_back(static_cast<char>(ch)); ret.push_back(static_cast<char>(ch));
@ -885,22 +891,22 @@ std::string wstring_to_string(const wide_string &src)
return ret; return ret;
} }
catch (utf8::invalid_utf8_exception&) { catch(utf8::invalid_utf8_exception&) {
ERR_GENERAL << "Invalid wide character string\n"; ERR_GENERAL << "Invalid UCS-4 character string\n";
return ret; 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); 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 { try {
utf8::iterator i1(src); utf8::iterator i1(src);
@ -912,7 +918,7 @@ wide_string string_to_wstring(const std::string &src)
++i1; ++i1;
} }
} }
catch (utf8::invalid_utf8_exception&) { catch(utf8::invalid_utf8_exception&) {
ERR_GENERAL << "Invalid UTF-8 string: \"" << src << "\"\n"; ERR_GENERAL << "Invalid UTF-8 string: \"" << src << "\"\n";
return res; return res;
} }
@ -920,13 +926,34 @@ wide_string string_to_wstring(const std::string &src)
return res; return res;
} }
utf16_string ucs4string_to_utf16string(const ucs4_string &src)
void truncate_as_wstring(std::string& str, const size_t size)
{ {
wide_string wide = utils::string_to_wstring(str); utf16_string res;
if(wide.size() > size) { const Uint32 bit17 = 0x10000;
wide.resize(size); BOOST_FOREACH(const ucs4char &u4, src) {
str = utils::wstring_to_string(wide); if(u4 < bit17)
res.push_back(static_cast<wchar_t>(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<wchar_t>(lead));
res.push_back(static_cast<wchar_t>(trail));
}
}
return res;
}
void truncate_as_ucs4string(std::string& str, const size_t size)
{
ucs4_string u4_str = utils::string_to_ucs4string(str);
if(u4_str.size() > size) {
u4_str.resize(size);
str = utils::ucs4string_to_string(u4_str);
} }
} }
@ -1002,7 +1029,7 @@ iterator& iterator::operator++()
return *this; return *this;
} }
wchar_t iterator::operator*() const ucs4char iterator::operator*() const
{ {
return current_char; return current_char;
} }
@ -1047,6 +1074,11 @@ void iterator::update()
current_char = (current_char << 6) | (static_cast<unsigned char>(*c) & 0x3F); current_char = (current_char << 6) | (static_cast<unsigned char>(*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();
} }
@ -1057,15 +1089,12 @@ utf8::string lowercase(const utf8::string& s)
std::string res; std::string res;
for(;itor != utf8::iterator::end(s); ++itor) { for(;itor != utf8::iterator::end(s); ++itor) {
#if defined(__APPLE__) || defined(__OpenBSD__) ucs4char uchar = *itor;
/** @todo FIXME: Should we support towupper on recent OSX platforms? */ // If wchar_t is less than 32 bits wide, we cannot apply towlower() to all codepoints
wchar_t uchar = *itor; if(uchar <= static_cast<ucs4char>(std::numeric_limits<wchar_t>::max()) &&
if(uchar >= 0 && uchar < 0x100) uchar >= static_cast<ucs4char>(std::numeric_limits<wchar_t>::min()))
uchar = tolower(uchar); uchar = towlower(static_cast<wchar_t>(uchar));
res += utils::wchar_to_string(uchar); res += utils::ucs4char_to_string(uchar);
#else
res += utils::wchar_to_string(towlower(*itor));
#endif
} }
res.append(itor.substr().second, s.end()); res.append(itor.substr().second, s.end());
@ -1125,4 +1154,4 @@ utf8::string& truncate(utf8::string& str, const size_t size)
return erase(str, size); return erase(str, size);
} }
} // end namespace utf8 } // end namespace utf8

View File

@ -26,13 +26,15 @@
#include "SDL_types.h" #include "SDL_types.h"
/** The type we use to represent Unicode strings. */ typedef Uint32 ucs4char;
typedef std::vector<wchar_t> wide_string; typedef std::vector<ucs4char> ucs4_string;
/** If we append a 0 to that one, we can pass it to SDL_ttf as a const Uint16*. */
typedef std::vector<Uint16> ucs2_string;
typedef std::vector<Uint32> ucs4_string;
namespace utf8 { typedef std::string string; } namespace utf8 { typedef std::string string; }
/**
* For win32 API.
* On windows, wchar_t is defined as Uint16
* Wide strings are expected to be UTF-16
*/
typedef std::vector<wchar_t> utf16_string;
class t_string; class t_string;
@ -315,9 +317,11 @@ bool isvalid_wildcard(const std::string &login);
typedef std::map< std::string, t_string > string_map; typedef std::map< std::string, t_string > string_map;
std::string wstring_to_string(const wide_string &); std::string ucs4string_to_string(const ucs4_string &);
wide_string string_to_wstring(const std::string &); ucs4_string string_to_ucs4string(const std::string &);
std::string wchar_to_string(const wchar_t); std::string ucs4char_to_string(const ucs4char);
utf16_string ucs4string_to_utf16string(const ucs4_string &);
/** /**
* Truncates a string. * Truncates a string.
@ -332,7 +336,7 @@ std::string wchar_to_string(const wchar_t);
* characters. * characters.
* @param size The size to truncate at. * @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. * Truncates a string to a given utf-8 character count and then appends an ellipsis.
@ -354,10 +358,10 @@ namespace utf8 {
{ {
public: public:
typedef std::input_iterator_tag iterator_category; typedef std::input_iterator_tag iterator_category;
typedef wchar_t value_type; typedef ucs4char value_type;
typedef ptrdiff_t difference_type; typedef ptrdiff_t difference_type;
typedef wchar_t* pointer; typedef ucs4char* pointer;
typedef wchar_t& reference; typedef ucs4char& reference;
iterator(const std::string& str); iterator(const std::string& str);
iterator(std::string::const_iterator const &begin, std::string::const_iterator const &end); iterator(std::string::const_iterator const &begin, std::string::const_iterator const &end);
@ -368,13 +372,13 @@ namespace utf8 {
bool operator==(const utf8::iterator& a) const; bool operator==(const utf8::iterator& a) const;
bool operator!=(const utf8::iterator& a) const { return ! (*this == a); } bool operator!=(const utf8::iterator& a) const { return ! (*this == a); }
iterator& operator++(); iterator& operator++();
wchar_t operator*() const; ucs4char operator*() const;
bool next_is_end(); bool next_is_end();
const std::pair<std::string::const_iterator, std::string::const_iterator>& substr() const; const std::pair<std::string::const_iterator, std::string::const_iterator>& substr() const;
private: private:
void update(); void update();
wchar_t current_char; ucs4char current_char;
std::string::const_iterator string_end; std::string::const_iterator string_end;
std::pair<std::string::const_iterator, std::string::const_iterator> current_substr; std::pair<std::string::const_iterator, std::string::const_iterator> current_substr;
}; };

View File

@ -29,12 +29,12 @@ const size_t max_message_length = 256;
void truncate_message(const simple_wml::string_span& str, simple_wml::node& message) 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 // 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<int>(chat_message::max_message_length)) { if(str.size() > static_cast<int>(chat_message::max_message_length)) {
std::string tmp(str.begin(), str.end()); 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. // 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()); message.set_attr_dup("message", tmp.c_str());
} }
} }

View File

@ -43,3 +43,32 @@ BOOST_AUTO_TEST_CASE( utils_join_test )
BOOST_CHECK( utf8::truncate(unicode,3) == "\xC3\xBCni"); // "üni" BOOST_CHECK( utf8::truncate(unicode,3) == "\xC3\xBCni"); // "üni"
} }
BOOST_AUTO_TEST_CASE( utils_unicode_test )
{
utf8::string apple_u8("apple");
ucs4_string apple_u4 = utils::string_to_ucs4string(apple_u8);
utf16_string apple_u16 = utils::ucs4string_to_utf16string(apple_u4);
BOOST_CHECK( apple_u4.size() == 5 );
BOOST_CHECK_EQUAL( apple_u8, utils::ucs4string_to_string(apple_u4) );
BOOST_CHECK_EQUAL( apple_u8.size(), apple_u16.size() );
ucs4_string water_u4;
water_u4.push_back(0x6C34);
utf8::string water_u8 = utils::ucs4string_to_string(water_u4);
utf16_string water_u16 = utils::ucs4string_to_utf16string(water_u4);
BOOST_CHECK_EQUAL(water_u4[0], water_u16[0]);
BOOST_CHECK_EQUAL(water_u8, "\u6C34");
utf8::string nonbmp_u8("\U00010000");
ucs4_string nonbmp_u4 = utils::string_to_ucs4string(nonbmp_u8);
utf16_string nonbmp_u16 = utils::ucs4string_to_utf16string(nonbmp_u4);
BOOST_CHECK_EQUAL(nonbmp_u8.size(), 4);
BOOST_CHECK_EQUAL(nonbmp_u4[0], 0x10000);
BOOST_CHECK_EQUAL(nonbmp_u16[0], 0xD800);
BOOST_CHECK_EQUAL(nonbmp_u16[1], 0xDC00);
BOOST_CHECK_EQUAL(nonbmp_u8, utils::ucs4string_to_string(nonbmp_u4));
}

View File

@ -199,14 +199,14 @@ unsigned ttext::insert_text(const unsigned offset, const std::string& text)
return len; return len;
} }
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)
{ {
const utf8::string insert = utils::wstring_to_string(unicode); const utf8::string insert = utils::ucs4string_to_string(unicode);
return insert_text(offset, insert); return insert_text(offset, insert);
} }
@ -293,8 +293,8 @@ bool ttext::set_text(const std::string& text, const bool markedup)
if(markedup != markedup_text_ || text != text_) { if(markedup != markedup_text_ || text != text_) {
assert(layout_); assert(layout_);
const wide_string wide = utils::string_to_wstring(text); const ucs4_string wide = utils::string_to_ucs4string(text);
const std::string narrow = utils::wstring_to_string(wide); const std::string narrow = utils::ucs4string_to_string(wide);
if(text != narrow) { if(text != narrow) {
ERR_GUI_L << "ttext::" << __func__ ERR_GUI_L << "ttext::" << __func__
<< " text '" << text << " text '" << text

View File

@ -16,6 +16,7 @@
#define TEXT_HPP_INCLUDED #define TEXT_HPP_INCLUDED
#include "sdl_utils.hpp" #include "sdl_utils.hpp"
#include "serialization/string_utils.hpp"
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
@ -101,7 +102,7 @@ public:
* *
* @returns True upon success, false otherwise. * @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. * Inserts unicode text.
@ -112,7 +113,7 @@ public:
* @returns The number of characters inserted. * @returns The number of characters inserted.
*/ */
unsigned insert_unicode( unsigned insert_unicode(
const unsigned offset, const std::vector<wchar_t>& unicode); const unsigned offset, const ucs4_string& unicode);
/***** ***** ***** ***** Font flags ***** ***** ***** *****/ /***** ***** ***** ***** Font flags ***** ***** ***** *****/

View File

@ -29,7 +29,7 @@ static lg::log_domain log_display("display");
namespace gui { 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) 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), cursor_(text_.size()), selstart_(-1), selend_(-1),
grabmouse_(false), text_pos_(0), editable_(editable), grabmouse_(false), text_pos_(0), editable_(editable),
show_cursor_(true), show_cursor_at_(0), text_image_(NULL), 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 textbox::text() const
{ {
const std::string &ret = utils::wstring_to_string(text_); const std::string &ret = utils::ucs4string_to_string(text_);
return ret; return ret;
} }
// set_text does not respect max_size_ // set_text does not respect max_size_
void textbox::set_text(const std::string& text, const SDL_Color& color) 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(); cursor_ = text_.size();
text_pos_ = 0; text_pos_ = 0;
selstart_ = -1; selstart_ = -1;
@ -94,7 +94,7 @@ void textbox::append_text(const std::string& text, bool auto_scroll, const SDL_C
return; return;
} }
const bool is_at_bottom = get_position() == get_max_position(); 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); const surface new_text = add_text_line(wtext, color);
surface new_surface = create_compatible_surface(text_image_,std::max<size_t>(text_image_->w,new_text->w),text_image_->h+new_text->h); surface new_surface = create_compatible_surface(text_image_,std::max<size_t>(text_image_->w,new_text->w),text_image_->h+new_text->h);
@ -285,7 +285,7 @@ void textbox::scroll(unsigned int pos)
set_dirty(true); 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_); line_height_ = font::get_max_height(font_size_);
@ -302,17 +302,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 // some more complex scripts (that is, RTL languages). This part of the work should
// actually be done by the font-rendering system. // actually be done by the font-rendering system.
std::string visible_string; 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()) { while(itor != text.end()) {
//If this is a space, save copies of the current state so we can roll back //If this is a space, save copies of the current state so we can roll back
if(char(*itor) == ' ') { if(char(*itor) == ' ') {
backup_itor = itor; backup_itor = itor;
} }
visible_string.append(utils::wchar_to_string(*itor)); visible_string.append(utils::ucs4char_to_string(*itor));
if(char(*itor) == '\n') { if(char(*itor) == '\n') {
backup_itor = text.end(); backup_itor = text.end();
@ -332,7 +332,7 @@ surface textbox::add_text_line(const wide_string& text, const SDL_Color& color)
} }
} }
backup_itor = text.end(); backup_itor = text.end();
wrapped_text.push_back(wchar_t('\n')); wrapped_text.push_back(ucs4char('\n'));
char_x_.push_back(0); char_x_.push_back(0);
char_y_.push_back(char_y_.back() + line_height_); char_y_.push_back(char_y_.back() + line_height_);
visible_string = ""; visible_string = "";
@ -344,7 +344,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)); const surface res(font::get_rendered_text(s, font_size_, color));
return res; return res;
@ -385,7 +385,7 @@ void textbox::erase_selection()
if(!is_selection()) if(!is_selection())
return; 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_)); text_.erase(itor, itor + abs(selend_ - selstart_));
cursor_ = std::min(selstart_, selend_); cursor_ = std::min(selstart_, selend_);
selstart_ = selend_ = -1; selstart_ = selend_ = -1;
@ -579,9 +579,9 @@ void textbox::handle_event(const SDL_Event& event, bool was_forwarded)
} }
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
wchar_t character = key.scancode; ucs4char character = key.scancode;
#else #else
wchar_t character = key.unicode; ucs4char character = key.unicode;
#endif #endif
//movement characters may have a "Unicode" field on some platforms, so ignore it. //movement characters may have a "Unicode" field on some platforms, so ignore it.
@ -614,7 +614,7 @@ void textbox::handle_event(const SDL_Event& event, bool was_forwarded)
//cut off anything after the first newline //cut off anything after the first newline
str.erase(std::find_if(str.begin(),str.end(),utils::isnewline),str.end()); 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(text_.size() < max_size_) {
if(s.size() + text_.size() > max_size_) { if(s.size() + text_.size() > max_size_) {
@ -635,8 +635,8 @@ void textbox::handle_event(const SDL_Event& event, bool was_forwarded)
const size_t beg = std::min<size_t>(size_t(selstart_),size_t(selend_)); const size_t beg = std::min<size_t>(size_t(selstart_),size_t(selend_));
const size_t end = std::max<size_t>(size_t(selstart_),size_t(selend_)); const size_t end = std::max<size_t>(size_t(selstart_),size_t(selend_));
wide_string ws = wide_string(text_.begin() + beg, text_.begin() + end); ucs4_string ws(text_.begin() + beg, text_.begin() + end);
std::string s = utils::wstring_to_string(ws); std::string s = utils::ucs4string_to_string(ws);
copy_to_clipboard(s, false); copy_to_clipboard(s, false);
} }
} }

View File

@ -55,13 +55,13 @@ protected:
virtual void scroll(unsigned int pos); virtual void scroll(unsigned int pos);
private: private:
virtual void handle_text_changed(const wide_string&) {} virtual void handle_text_changed(const ucs4_string&) {}
size_t max_size_; size_t max_size_;
int font_size_; int font_size_;
wide_string text_; ucs4_string text_;
// mutable unsigned int firstOnScreen_; // mutable unsigned int firstOnScreen_;
int cursor_; int cursor_;
@ -100,7 +100,7 @@ private:
void draw_cursor(int pos, CVideo &video) const; void draw_cursor(int pos, CVideo &video) const;
void update_text_cache(bool reset = false, const SDL_Color& color =font::NORMAL_COLOR); 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(); bool is_selection();
void erase_selection(); void erase_selection();

View File

@ -1,210 +1,217 @@
/* /*
Copyright (C) 2013 - 2014 by Maxim Biro <nurupo.contributions@gmail.com> Copyright (C) 2013 - 2014 by Maxim Biro <nurupo.contributions@gmail.com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/ Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY. but WITHOUT ANY WARRANTY.
See the COPYING file for more details. See the COPYING file for more details.
*/ */
#include "windows_tray_notification.hpp" #include "windows_tray_notification.hpp"
#include <SDL_syswm.h> #include <SDL_syswm.h>
#include "gettext.hpp" #include "gettext.hpp"
#include "serialization/string_utils.hpp" #include "serialization/string_utils.hpp"
NOTIFYICONDATA* windows_tray_notification::nid = NULL; NOTIFYICONDATA* windows_tray_notification::nid = NULL;
bool windows_tray_notification::message_reset = false; bool windows_tray_notification::message_reset = false;
void windows_tray_notification::destroy_tray_icon() void windows_tray_notification::destroy_tray_icon()
{ {
if (nid == NULL) { if (nid == NULL) {
return; return;
} }
if (!message_reset){ if (!message_reset){
Shell_NotifyIcon(NIM_DELETE, nid); Shell_NotifyIcon(NIM_DELETE, nid);
delete nid; delete nid;
nid = NULL; nid = NULL;
} else { } else {
message_reset = false; message_reset = false;
} }
} }
void windows_tray_notification::handle_system_event(const SDL_Event& event) void windows_tray_notification::handle_system_event(const SDL_Event& event)
{ {
if (event.syswm.msg->msg != WM_TRAYNOTIFY) { if (event.syswm.msg->msg != WM_TRAYNOTIFY) {
return; return;
} }
if (event.syswm.msg->lParam == NIN_BALLOONUSERCLICK) { if (event.syswm.msg->lParam == NIN_BALLOONUSERCLICK) {
switch_to_wesnoth_window(); switch_to_wesnoth_window();
destroy_tray_icon(); destroy_tray_icon();
} else if (event.syswm.msg->lParam == NIN_BALLOONTIMEOUT) { } else if (event.syswm.msg->lParam == NIN_BALLOONTIMEOUT) {
destroy_tray_icon(); destroy_tray_icon();
} }
} }
bool windows_tray_notification::create_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 // getting handle to a 32x32 icon, contained in "WESNOTH_ICON" icon group of wesnoth.exe resources
const HMODULE wesnoth_exe = GetModuleHandle(NULL); const HMODULE wesnoth_exe = GetModuleHandle(NULL);
if (wesnoth_exe == NULL) { if (wesnoth_exe == NULL) {
return false; return false;
} }
const HRSRC group_icon_info = FindResource(wesnoth_exe, L"WESNOTH_ICON", RT_GROUP_ICON); const HRSRC group_icon_info = FindResource(wesnoth_exe, L"WESNOTH_ICON", RT_GROUP_ICON);
if (group_icon_info == NULL) { if (group_icon_info == NULL) {
return false; return false;
} }
HGLOBAL hGlobal = LoadResource(wesnoth_exe, group_icon_info); HGLOBAL hGlobal = LoadResource(wesnoth_exe, group_icon_info);
if (hGlobal == NULL) { if (hGlobal == NULL) {
return false; return false;
} }
const PBYTE group_icon_res = static_cast<PBYTE>(LockResource(hGlobal)); const PBYTE group_icon_res = static_cast<PBYTE>(LockResource(hGlobal));
if (group_icon_res == NULL) { if (group_icon_res == NULL) {
return false; return false;
} }
const int nID = LookupIconIdFromDirectoryEx(group_icon_res, TRUE, 32, 32, LR_DEFAULTCOLOR); const int nID = LookupIconIdFromDirectoryEx(group_icon_res, TRUE, 32, 32, LR_DEFAULTCOLOR);
if (nID == 0) { if (nID == 0) {
return false; return false;
} }
const HRSRC icon_info = FindResource(wesnoth_exe, MAKEINTRESOURCE(nID), MAKEINTRESOURCE(3)); const HRSRC icon_info = FindResource(wesnoth_exe, MAKEINTRESOURCE(nID), MAKEINTRESOURCE(3));
if (icon_info == NULL) { if (icon_info == NULL) {
return false; return false;
} }
hGlobal = LoadResource(wesnoth_exe, icon_info); hGlobal = LoadResource(wesnoth_exe, icon_info);
if (hGlobal == NULL) { if (hGlobal == NULL) {
return false; return false;
} }
const PBYTE icon_res = static_cast<PBYTE>(LockResource(hGlobal)); const PBYTE icon_res = static_cast<PBYTE>(LockResource(hGlobal));
if (icon_res == NULL) { if (icon_res == NULL) {
return false; return false;
} }
const HICON icon = CreateIconFromResource(icon_res, SizeofResource(wesnoth_exe, icon_info), TRUE, 0x00030000); const HICON icon = CreateIconFromResource(icon_res, SizeofResource(wesnoth_exe, icon_info), TRUE, 0x00030000);
if (icon == NULL) { if (icon == NULL) {
return false; return false;
} }
const HWND window = get_window_handle(); const HWND window = get_window_handle();
if (window == NULL) { if (window == NULL) {
return false; return false;
} }
const std::wstring& wtip = string_to_wstring(_("The Battle for Wesnoth")); const std::wstring& wtip = string_to_wstring(_("The Battle for Wesnoth"), 63);
// filling notification structure // filling notification structure
nid = new NOTIFYICONDATA; nid = new NOTIFYICONDATA;
memset(nid, 0, sizeof(&nid)); memset(nid, 0, sizeof(&nid));
nid->cbSize = NOTIFYICONDATA_V2_SIZE; nid->cbSize = NOTIFYICONDATA_V2_SIZE;
nid->hWnd = window; nid->hWnd = window;
nid->uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE; nid->uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
nid->dwInfoFlags = NIIF_USER; nid->dwInfoFlags = NIIF_USER;
nid->uVersion = NOTIFYICON_VERSION; nid->uVersion = NOTIFYICON_VERSION;
nid->uCallbackMessage = WM_TRAYNOTIFY; nid->uCallbackMessage = WM_TRAYNOTIFY;
nid->uID = ICON_ID; nid->uID = ICON_ID;
nid->hIcon = icon; nid->hIcon = icon;
#if _WIN32_WINNT >= 0x600 #if _WIN32_WINNT >= 0x600
nid->hBalloonIcon = icon; nid->hBalloonIcon = icon;
#endif #endif
lstrcpy(nid->szTip, wtip.c_str()); lstrcpy(nid->szTip, wtip.c_str());
// creating icon notification // creating icon notification
return Shell_NotifyIcon(NIM_ADD, nid) != FALSE; return Shell_NotifyIcon(NIM_ADD, nid) != FALSE;
} }
bool windows_tray_notification::set_tray_message(const std::string& title, const std::string& message) bool windows_tray_notification::set_tray_message(const std::string& title, const std::string& message)
{ {
// prevents deletion of icon when resetting already existing notification // prevents deletion of icon when resetting already existing notification
message_reset = (nid->uFlags & NIF_INFO) != 0; message_reset = (nid->uFlags & NIF_INFO) != 0;
nid->uFlags |= NIF_INFO; nid->uFlags |= NIF_INFO;
lstrcpy(nid->szInfoTitle, string_to_wstring(title).c_str()); lstrcpy(nid->szInfoTitle, string_to_wstring(title, 63).c_str());
lstrcpy(nid->szInfo, string_to_wstring(message).c_str()); lstrcpy(nid->szInfo, string_to_wstring(message, 255).c_str());
// setting notification // setting notification
return Shell_NotifyIcon(NIM_MODIFY, nid) != FALSE; return Shell_NotifyIcon(NIM_MODIFY, nid) != FALSE;
} }
void windows_tray_notification::adjust_length(std::string& title, std::string& message) void windows_tray_notification::adjust_length(std::string& title, std::string& message)
{ {
static const int ELIPSIS_LENGTH = 3; static const int ELIPSIS_LENGTH = 3;
// limitations set by winapi // limitations set by winapi
if (title.length() > MAX_TITLE_LENGTH) { if (title.length() > MAX_TITLE_LENGTH) {
utils::ellipsis_truncate(title, MAX_TITLE_LENGTH - ELIPSIS_LENGTH); utils::ellipsis_truncate(title, MAX_TITLE_LENGTH - ELIPSIS_LENGTH);
} }
if (message.length() > MAX_MESSAGE_LENGTH) { if (message.length() > MAX_MESSAGE_LENGTH) {
utils::ellipsis_truncate(message, MAX_MESSAGE_LENGTH - ELIPSIS_LENGTH); utils::ellipsis_truncate(message, MAX_MESSAGE_LENGTH - ELIPSIS_LENGTH);
} }
} }
HWND windows_tray_notification::get_window_handle() HWND windows_tray_notification::get_window_handle()
{ {
SDL_SysWMinfo wmInfo; SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version); SDL_VERSION(&wmInfo.version);
if (SDL_GetWMInfo(&wmInfo) != 1) { if (SDL_GetWMInfo(&wmInfo) != 1) {
return NULL; return NULL;
} }
return wmInfo.window; return wmInfo.window;
} }
void windows_tray_notification::switch_to_wesnoth_window() void windows_tray_notification::switch_to_wesnoth_window()
{ {
const HWND window = get_window_handle(); const HWND window = get_window_handle();
if (window == NULL) { if (window == NULL) {
return; return;
} }
if (IsIconic(window)) { if (IsIconic(window)) {
ShowWindow(window, SW_RESTORE); ShowWindow(window, SW_RESTORE);
} }
SetForegroundWindow(window); SetForegroundWindow(window);
} }
std::wstring windows_tray_notification::string_to_wstring(const std::string& string) std::wstring windows_tray_notification::string_to_wstring(const std::string& string, size_t maxlength)
{ {
const std::vector<wchar_t> wide_string = utils::string_to_wstring(string); const utils::ucs4_string u4_string = utils::string_to_ucs4string(string);
return std::wstring(wide_string.begin(), wide_string.end()); utils::utf16_string u16_string = utils::ucs4string_to_utf16string(u4_string);
} if(u16_string.size() > maxlength) {
if((u16_string[maxlength-1] & 0xDC00) == 0xD800)
bool windows_tray_notification::show(std::string title, std::string message) u16_string.resize(maxlength - 1);
{ else
adjust_length(title, message); u16_string.resize(maxlength);
}
const bool tray_icon_exist = nid != NULL; return std::wstring(utf16_string.begin(), utf16_string.end());
if (!tray_icon_exist) { }
const bool tray_icon_created = create_tray_icon();
if (!tray_icon_created) { bool windows_tray_notification::show(std::string title, std::string message)
const bool memory_allocated = nid != NULL; {
if (memory_allocated) { adjust_length(title, message);
destroy_tray_icon();
} const bool tray_icon_exist = nid != NULL;
return false; if (!tray_icon_exist) {
} const bool tray_icon_created = create_tray_icon();
} if (!tray_icon_created) {
const bool memory_allocated = nid != NULL;
// at this point tray icon was just created or already existed before, so it's safe to call `set_tray_message` if (memory_allocated) {
destroy_tray_icon();
const bool result = set_tray_message(title, message); }
// the `destroy_tray_icon` will be called by event only if `set_tray_message` succeeded return false;
// if it doesn't succeed, we have to call `destroy_tray_icon` manually }
if (!result) { }
destroy_tray_icon();
} // at this point tray icon was just created or already existed before, so it's safe to call `set_tray_message`
return result;
} 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;
}

View File

@ -1,83 +1,83 @@
/* /*
Copyright (C) 2013 - 2014 by Maxim Biro <nurupo.contributions@gmail.com> Copyright (C) 2013 - 2014 by Maxim Biro <nurupo.contributions@gmail.com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/ Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY. but WITHOUT ANY WARRANTY.
See the COPYING file for more details. See the COPYING file for more details.
*/ */
#ifndef WINDOWS_TRAY_NOTIFICATION_HPP_INCLUDED #ifndef WINDOWS_TRAY_NOTIFICATION_HPP_INCLUDED
#define WINDOWS_TRAY_NOTIFICATION_HPP_INCLUDED #define WINDOWS_TRAY_NOTIFICATION_HPP_INCLUDED
#include <SDL.h> #include <SDL.h>
#include <string> #include <string>
//forces to call Unicode winapi functions instead of ASCII (default) //forces to call Unicode winapi functions instead of ASCII (default)
#define UNICODE #define UNICODE
//defines that mingw misses //defines that mingw misses
#ifndef _WIN32_IE #ifndef _WIN32_IE
#define _WIN32_IE 0x0600 //specifying target platform to be Windows XP and higher #define _WIN32_IE 0x0600 //specifying target platform to be Windows XP and higher
#endif #endif
#ifndef NIIF_USER #ifndef NIIF_USER
#define NIIF_USER 0x00000004 #define NIIF_USER 0x00000004
#endif #endif
#ifndef NIN_BALLOONTIMEOUT #ifndef NIN_BALLOONTIMEOUT
#define NIN_BALLOONTIMEOUT (WM_USER + 4) #define NIN_BALLOONTIMEOUT (WM_USER + 4)
#endif #endif
#ifndef NIN_BALLOONUSERCLICK #ifndef NIN_BALLOONUSERCLICK
#define NIN_BALLOONUSERCLICK (WM_USER + 5) #define NIN_BALLOONUSERCLICK (WM_USER + 5)
#endif #endif
// ShellAPI.h should be included after Windows.h only! // ShellAPI.h should be included after Windows.h only!
#include <windows.h> #include <windows.h>
#include <shellapi.h> #include <shellapi.h>
class windows_tray_notification { class windows_tray_notification {
public: public:
/** /**
* Displays a tray notification. * Displays a tray notification.
* When user clicks on the notification popup, the user switches to the wesnoth window. * 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 * @param title Title of a notification. Gets truncated if longer than 64 characters, including
* the terminating null character. * the terminating null character.
* @param message Message of a notification. Gets truncated if longer than 256 characters, including * @param message Message of a notification. Gets truncated if longer than 256 characters, including
* the terminating null character. * the terminating null character.
* *
* @return True if message was shown successfully, False otherwise. * @return True if message was shown successfully, False otherwise.
*/ */
static bool show(std::string title, std::string message); static bool show(std::string title, std::string message);
/** /**
* Frees resources when a notification disappears, switches user to the wesnoth * Frees resources when a notification disappears, switches user to the wesnoth
* window if the notification popup was clicked by user. * window if the notification popup was clicked by user.
* *
* @param event System event. * @param event System event.
*/ */
static void handle_system_event(const SDL_Event& event); static void handle_system_event(const SDL_Event& event);
private: private:
static NOTIFYICONDATA* nid; static NOTIFYICONDATA* nid;
static bool message_reset; static bool message_reset;
static const int ICON_ID = 1007; // just a random number static const int ICON_ID = 1007; // just a random number
static const unsigned int WM_TRAYNOTIFY = 32868; // WM_APP+100 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_TITLE_LENGTH = 63; // 64 including the terminating null character
static const size_t MAX_MESSAGE_LENGTH = 255; // 256 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 bool create_tray_icon();
static void destroy_tray_icon(); static void destroy_tray_icon();
static bool set_tray_message(const std::string& title, const std::string& message); static bool set_tray_message(const std::string& title, const std::string& message);
static void adjust_length(std::string& title, std::string& message); static void adjust_length(std::string& title, std::string& message);
static HWND get_window_handle(); static HWND get_window_handle();
static void switch_to_wesnoth_window(); static void switch_to_wesnoth_window();
static std::wstring string_to_wstring(const std::string& string); static std::wstring string_to_wstring(const std::string& string);
explicit windows_tray_notification(); explicit windows_tray_notification();
windows_tray_notification(const windows_tray_notification& w); windows_tray_notification(const windows_tray_notification& w);
windows_tray_notification& operator=(const windows_tray_notification& w); windows_tray_notification& operator=(const windows_tray_notification& w);
}; };
#endif // WINDOWS_TRAY_NOTIFICATION_HPP_INCLUDED #endif // WINDOWS_TRAY_NOTIFICATION_HPP_INCLUDED