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
// 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 =

View File

@ -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;

View File

@ -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 {

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";
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;

View File

@ -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;
}

View File

@ -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();

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/
@ -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) {

View File

@ -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);

View File

@ -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));
}

View File

@ -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
{

View File

@ -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

View File

@ -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;
};

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)
{
// 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());
}
}

View File

@ -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));
}

View File

@ -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

View File

@ -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 ***** ***** ***** *****/

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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