mirror of
https://github.com/wesnoth/wesnoth
synced 2025-04-30 16:33:31 +00:00
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:
commit
b0d31d07e3
@ -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 =
|
||||
|
@ -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<std::string> words = utils::split(utils::wstring_to_string(text),' ');
|
||||
void filter_textbox::handle_text_changed(const ucs4_string& text) {
|
||||
const std::vector<std::string> words = utils::split(utils::ucs4string_to_string(text),' ');
|
||||
if (words == last_words)
|
||||
return;
|
||||
last_words = words;
|
||||
|
@ -145,7 +145,7 @@ private:
|
||||
std::vector<std::string> 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 {
|
||||
|
@ -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<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) {
|
||||
ERR_DU << "open_object(): ShellExecute() failed (" << res << ")\n";
|
||||
return false;
|
||||
|
@ -3345,9 +3345,9 @@ std::string get_first_word(const std::string &s)
|
||||
if (ch == 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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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<unsigned int>(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<wchar_t>(0x100)
|
||||
while(ch != end && *ch < static_cast<ucs4char>(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 != utf8::iterator::end(unwrapped_text) &&
|
||||
*ch != ' ' && *ch != '\n'; ++ch) {
|
||||
|
||||
|
@ -22,6 +22,7 @@ class CVideo;
|
||||
struct surface;
|
||||
#include <SDL_video.h>
|
||||
#include <string>
|
||||
#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);
|
||||
|
24
src/race.cpp
24
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<std::string>& items,
|
||||
markov_prefix_map res;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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<wchar_t>(0))
|
||||
if (std::find(i->second.begin(), i->second.end(), static_cast<ucs4char>(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));
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ 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
|
||||
{
|
||||
|
@ -25,13 +25,35 @@
|
||||
#include "log.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
#include "util.hpp"
|
||||
#include <limits>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
static lg::log_domain log_engine("engine");
|
||||
#define ERR_GENERAL LOG_STREAM(err, lg::general)
|
||||
#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 {
|
||||
|
||||
const std::string ellipsis = "...";
|
||||
@ -840,33 +862,17 @@ std::vector< std::pair< int, int > > parse_ranges(std::string const &str)
|
||||
return to_return;
|
||||
}
|
||||
|
||||
|
||||
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<Uint32>(ch) >= bitmask) {
|
||||
count++;
|
||||
}
|
||||
|
||||
bitmask <<= 5;
|
||||
}
|
||||
|
||||
if(count > 6) {
|
||||
throw utf8::invalid_utf8_exception();
|
||||
}
|
||||
count = byte_size_from_ucs4_codepoint(ch);
|
||||
|
||||
if(count == 1) {
|
||||
ret.push_back(static_cast<char>(ch));
|
||||
@ -885,22 +891,22 @@ std::string wstring_to_string(const wide_string &src)
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch (utf8::invalid_utf8_exception&) {
|
||||
ERR_GENERAL << "Invalid wide character string\n";
|
||||
catch(utf8::invalid_utf8_exception&) {
|
||||
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);
|
||||
@ -912,7 +918,7 @@ wide_string string_to_wstring(const std::string &src)
|
||||
++i1;
|
||||
}
|
||||
}
|
||||
catch (utf8::invalid_utf8_exception&) {
|
||||
catch(utf8::invalid_utf8_exception&) {
|
||||
ERR_GENERAL << "Invalid UTF-8 string: \"" << src << "\"\n";
|
||||
return res;
|
||||
}
|
||||
@ -920,13 +926,34 @@ wide_string string_to_wstring(const std::string &src)
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void truncate_as_wstring(std::string& str, const size_t size)
|
||||
utf16_string ucs4string_to_utf16string(const ucs4_string &src)
|
||||
{
|
||||
wide_string wide = utils::string_to_wstring(str);
|
||||
if(wide.size() > size) {
|
||||
wide.resize(size);
|
||||
str = utils::wstring_to_string(wide);
|
||||
utf16_string res;
|
||||
const Uint32 bit17 = 0x10000;
|
||||
BOOST_FOREACH(const ucs4char &u4, src) {
|
||||
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;
|
||||
}
|
||||
|
||||
wchar_t iterator::operator*() const
|
||||
ucs4char iterator::operator*() const
|
||||
{
|
||||
return current_char;
|
||||
}
|
||||
@ -1047,6 +1074,11 @@ void iterator::update()
|
||||
|
||||
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;
|
||||
|
||||
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<ucs4char>(std::numeric_limits<wchar_t>::max()) &&
|
||||
uchar >= static_cast<ucs4char>(std::numeric_limits<wchar_t>::min()))
|
||||
uchar = towlower(static_cast<wchar_t>(uchar));
|
||||
res += utils::ucs4char_to_string(uchar);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
} // end namespace utf8
|
||||
} // end namespace utf8
|
||||
|
@ -26,13 +26,15 @@
|
||||
|
||||
#include "SDL_types.h"
|
||||
|
||||
/** The type we use to represent Unicode strings. */
|
||||
typedef std::vector<wchar_t> wide_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;
|
||||
typedef Uint32 ucs4char;
|
||||
typedef std::vector<ucs4char> ucs4_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;
|
||||
|
||||
@ -315,9 +317,11 @@ bool isvalid_wildcard(const std::string &login);
|
||||
|
||||
typedef std::map< std::string, t_string > string_map;
|
||||
|
||||
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 &);
|
||||
|
||||
/**
|
||||
* Truncates a string.
|
||||
@ -332,7 +336,7 @@ std::string wchar_to_string(const wchar_t);
|
||||
* 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.
|
||||
@ -354,10 +358,10 @@ namespace utf8 {
|
||||
{
|
||||
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;
|
||||
|
||||
iterator(const std::string& str);
|
||||
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 { return ! (*this == a); }
|
||||
iterator& operator++();
|
||||
wchar_t operator*() const;
|
||||
ucs4char operator*() const;
|
||||
bool next_is_end();
|
||||
const std::pair<std::string::const_iterator, std::string::const_iterator>& substr() const;
|
||||
private:
|
||||
void update();
|
||||
|
||||
wchar_t current_char;
|
||||
ucs4char current_char;
|
||||
std::string::const_iterator string_end;
|
||||
std::pair<std::string::const_iterator, std::string::const_iterator> current_substr;
|
||||
};
|
||||
|
@ -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<int>(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());
|
||||
}
|
||||
}
|
||||
|
@ -43,3 +43,32 @@ BOOST_AUTO_TEST_CASE( utils_join_test )
|
||||
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));
|
||||
}
|
||||
|
||||
|
12
src/text.cpp
12
src/text.cpp
@ -199,14 +199,14 @@ unsigned ttext::insert_text(const unsigned offset, const std::string& text)
|
||||
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);
|
||||
}
|
||||
|
||||
@ -293,8 +293,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
|
||||
|
@ -16,6 +16,7 @@
|
||||
#define TEXT_HPP_INCLUDED
|
||||
|
||||
#include "sdl_utils.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
@ -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<wchar_t>& unicode);
|
||||
const unsigned offset, const ucs4_string& unicode);
|
||||
|
||||
/***** ***** ***** ***** Font flags ***** ***** ***** *****/
|
||||
|
||||
|
@ -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<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);
|
||||
}
|
||||
|
||||
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_);
|
||||
|
||||
@ -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
|
||||
// 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();
|
||||
@ -332,7 +332,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 = "";
|
||||
@ -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));
|
||||
|
||||
return res;
|
||||
@ -385,7 +385,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;
|
||||
@ -579,9 +579,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.
|
||||
@ -614,7 +614,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_) {
|
||||
@ -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 end = std::max<size_t>(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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -1,210 +1,217 @@
|
||||
/*
|
||||
Copyright (C) 2013 - 2014 by Maxim Biro <nurupo.contributions@gmail.com>
|
||||
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 <SDL_syswm.h>
|
||||
|
||||
#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<PBYTE>(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<PBYTE>(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<wchar_t> 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 <nurupo.contributions@gmail.com>
|
||||
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 <SDL_syswm.h>
|
||||
|
||||
#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<PBYTE>(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<PBYTE>(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"), 63);
|
||||
|
||||
// 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, 63).c_str());
|
||||
lstrcpy(nid->szInfo, string_to_wstring(message, 255).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, size_t maxlength)
|
||||
{
|
||||
const utils::ucs4_string u4_string = utils::string_to_ucs4string(string);
|
||||
utils::utf16_string u16_string = utils::ucs4string_to_utf16string(u4_string);
|
||||
if(u16_string.size() > maxlength) {
|
||||
if((u16_string[maxlength-1] & 0xDC00) == 0xD800)
|
||||
u16_string.resize(maxlength - 1);
|
||||
else
|
||||
u16_string.resize(maxlength);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -1,83 +1,83 @@
|
||||
/*
|
||||
Copyright (C) 2013 - 2014 by Maxim Biro <nurupo.contributions@gmail.com>
|
||||
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 <SDL.h>
|
||||
#include <string>
|
||||
//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 <windows.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
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 <nurupo.contributions@gmail.com>
|
||||
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 <SDL.h>
|
||||
#include <string>
|
||||
//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 <windows.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user