Merge branch 'hotfix-1.7.3' into develop

This commit is contained in:
Jinhao 2020-04-21 00:16:40 +08:00
commit 1e19322ad1
17 changed files with 733 additions and 258 deletions

View File

@ -114,10 +114,13 @@ endforeach()
### Some nana compilation options ### ### Some nana compilation options ###
option(NANA_CMAKE_AUTOMATIC_GUI_TESTING "Activate automatic GUI testing?" OFF) option(NANA_CMAKE_AUTOMATIC_GUI_TESTING "Activate automatic GUI testing?" OFF)
option(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ "replaced boost.thread with meganz's mingw-std-threads." OFF) # deprecate? option(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ "replaced boost.thread with meganz's mingw-std-threads." OFF) # deprecate?
option(NANA_CMAKE_ENABLE_CONF "enable config.hpp" OFF)
######## Nana options ######## Nana options
target_compile_definitions(nana PRIVATE NANA_IGNORE_CONF) # really ?? if(NOT NANA_CMAKE_ENABLE_CONF)
target_compile_definitions(nana PRIVATE NANA_IGNORE_CONF) # really ??
endif()
if(NANA_CMAKE_AUTOMATIC_GUI_TESTING) if(NANA_CMAKE_AUTOMATIC_GUI_TESTING)
target_compile_definitions(nana PUBLIC NANA_AUTOMATIC_GUI_TESTING) target_compile_definitions(nana PUBLIC NANA_AUTOMATIC_GUI_TESTING)
# todo: enable_testing() # ?? # todo: enable_testing() # ??

View File

@ -1,7 +1,7 @@
/** /**
* A Categorize Implementation * A Categorize Implementation
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -248,7 +248,7 @@ namespace nana
/// Retrieves a reference of the current category's value type object. If current category is empty, it throws a exception of std::runtime_error. /// Retrieves a reference of the current category's value type object. If current category is empty, it throws a exception of std::runtime_error.
value_type& value() const value_type& value() const
{ {
return this->get_drawer_trigger().value(); return nana::any_cast<value_type&>(this->get_drawer_trigger().value());
} }
private: private:
//Overrides widget's virtual functions //Overrides widget's virtual functions

View File

@ -1,7 +1,7 @@
/** /**
* A List Box Implementation * A List Box Implementation
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -1511,8 +1511,18 @@ the nana::detail::basic_window member pointer scheme
/// Returns the index pair of the item which contains the specified "screen" point. /// Returns the index pair of the item which contains the specified "screen" point.
index_pair cast(const point & screen_pos) const; index_pair cast(const point & screen_pos) const;
/// Converts the index between absolute position and display position
/**
* @param idx The index to be converted
* @param from_display_order If this parameter is true, the method converts a display position to an absolute position.
* If the parameter is false, the method converts an absolute position to a display position.
* @return a display position or an absolute position that are depending on from_display_order.
*/
index_pair index_cast(index_pair idx, bool from_display_order) const;
/// Returns the item which is hovered /// Returns the item which is hovered
/** /**
* The item position is an absolute position.
* @param return_end Indicates whether to return an end position instead of an empty position if no item is hovered. * @param return_end Indicates whether to return an end position instead of an empty position if no item is hovered.
* @return The position of the hovered item. If return_end is true and no item is hovered it returns the position next to the last item of last category. * @return The position of the hovered item. If return_end is true and no item is hovered it returns the position next to the last item of last category.
*/ */

View File

@ -1,7 +1,7 @@
/* /*
* Text Token Stream * Text Token Stream
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -20,6 +20,7 @@
#include <list> #include <list>
#include <stack> #include <stack>
#include <stdexcept> #include <stdexcept>
#include <algorithm>
#include <nana/push_ignore_diagnostic> #include <nana/push_ignore_diagnostic>
#include <nana/unicode_bidi.hpp> #include <nana/unicode_bidi.hpp>
@ -41,13 +42,14 @@ namespace nana{ namespace widgets{ namespace skeletons
eof eof
}; };
class tokenizer class tokenizer
{ {
public: public:
tokenizer(const std::wstring& s, bool format_enabled) tokenizer(const std::wstring& s, bool format_enabled) :
: iptr_(s.data()), iptr_(s.data()),
endptr_(s.data() + s.size()), endptr_(s.data() + s.size()),
format_enabled_(format_enabled) format_enabled_(format_enabled)
{ {
} }
@ -59,18 +61,18 @@ namespace nana{ namespace widgets{ namespace skeletons
//Read the token. //Read the token.
token read() token read()
{ {
if(revert_token_ != token::eof) if (revert_token_ != token::eof)
{ {
token tk = revert_token_; token tk = revert_token_;
revert_token_ = token::eof; revert_token_ = token::eof;
return tk; return tk;
} }
if(iptr_ == endptr_) if (iptr_ == endptr_)
return token::eof; return token::eof;
//Check whether it is a format token. //Check whether it is a format token.
if(format_enabled_ && format_state_) if (format_enabled_ && format_state_)
return _m_format_token(); return _m_format_token();
return _m_token(); return _m_token();
@ -107,8 +109,7 @@ namespace nana{ namespace widgets{ namespace skeletons
token _m_token() token _m_token()
{ {
wchar_t ch = *iptr_; wchar_t ch = *iptr_;
if (ch > 0xFF)
if(ch > 0xFF)
{ {
//This is the Unicode. //This is the Unicode.
@ -122,53 +123,58 @@ namespace nana{ namespace widgets{ namespace skeletons
} }
ch = *++iptr_; ch = *++iptr_;
while((iptr_ != endptr_) && (ch > 0xFF) && (false == _m_unicode_word_breakable(iptr_))) while ((iptr_ != endptr_) && (ch > 0xFF) && (false == _m_unicode_word_breakable(iptr_)))
{ {
idstr_.append(1, ch); idstr_.append(1, ch);
ch = *++iptr_; ch = *++iptr_;
} }
//When the last _m_unicode_word_breakable returns true, it implies the ch(left character)
//is not the breakable character. So it belongs to the data.
idstr_.append(1, ch);
++iptr_;
return token::data; return token::data;
} }
if('\n' == ch) if ('\n' == ch)
{ {
++iptr_; ++iptr_;
return token::endl; return token::endl;
} }
if(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'))
{ {
auto idstr = iptr_; auto idstr = iptr_;
do do
{ {
ch = *(++iptr_); ch = *(++iptr_);
} } while (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'));
while(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'));
idstr_.assign(idstr, iptr_); idstr_.assign(idstr, iptr_);
return token::data; return token::data;
} }
if('0' <= ch && ch <= '9') if ('0' <= ch && ch <= '9')
{ {
_m_read_number(); _m_read_number();
return token::data; return token::data;
} }
if(('<' == ch) && format_enabled_) if (('<' == ch) && format_enabled_)
{ {
//pos keeps the current position, and it used for restring //pos keeps the current position, and it used for restoring
//iptr_ when the search is failed. //iptr_ when the search is failed.
auto pos = ++iptr_; auto pos = ++iptr_;
_m_eat_whitespace(); _m_eat_whitespace();
if(*iptr_ == '/') if (*iptr_ == '/')
{ {
++iptr_; ++iptr_;
_m_eat_whitespace(); _m_eat_whitespace();
if(*iptr_ == '>') if (*iptr_ == '>')
{ {
++iptr_; ++iptr_;
return token::format_end; return token::format_end;
@ -182,11 +188,10 @@ namespace nana{ namespace widgets{ namespace skeletons
return token::tag_begin; return token::tag_begin;
} }
//Escape //Escape
if(this->format_enabled_ && (ch == '\\')) if (this->format_enabled_ && (ch == '\\'))
{ {
if(iptr_ + 1 < endptr_) if (iptr_ + 1 < endptr_)
{ {
ch = *(iptr_ + 1); ch = *(iptr_ + 1);
@ -221,8 +226,7 @@ namespace nana{ namespace widgets{ namespace skeletons
_m_eat_whitespace(); _m_eat_whitespace();
auto ch = *iptr_++; auto ch = *iptr_++;
switch (ch)
switch(ch)
{ {
case ',': return token::comma; case ',': return token::comma;
case '/': return token::backslash; case '/': return token::backslash;
@ -232,40 +236,40 @@ namespace nana{ namespace widgets{ namespace skeletons
return token::tag_end; return token::tag_end;
case '"': case '"':
//Here is a string and all the meta characters will be ignored except " //Here is a string and all the meta characters will be ignored except "
{ {
auto str = iptr_; auto str = iptr_;
while((iptr_ != endptr_) && (*iptr_ != '"')) while ((iptr_ != endptr_) && (*iptr_ != '"'))
++iptr_; ++iptr_;
idstr_.assign(str, iptr_++); idstr_.assign(str, iptr_++);
} }
return token::string; return token::string;
case '(': case '(':
_m_eat_whitespace(); _m_eat_whitespace();
if((iptr_ < endptr_) && _m_is_idstr_element(*iptr_)) if ((iptr_ < endptr_) && _m_is_idstr_element(*iptr_))
{ {
auto pbegin = iptr_; auto pbegin = iptr_;
while((iptr_ < endptr_) && _m_is_idstr_element(*iptr_)) while ((iptr_ < endptr_) && _m_is_idstr_element(*iptr_))
++iptr_; ++iptr_;
binary_.first.assign(pbegin, iptr_); binary_.first.assign(pbegin, iptr_);
_m_eat_whitespace(); _m_eat_whitespace();
if((iptr_ < endptr_) && (',' == *iptr_)) if ((iptr_ < endptr_) && (',' == *iptr_))
{ {
++iptr_; ++iptr_;
_m_eat_whitespace(); _m_eat_whitespace();
if((iptr_ < endptr_) && _m_is_idstr_element(*iptr_)) if ((iptr_ < endptr_) && _m_is_idstr_element(*iptr_))
{ {
pbegin = iptr_; pbegin = iptr_;
while((iptr_ < endptr_) && _m_is_idstr_element(*iptr_)) while ((iptr_ < endptr_) && _m_is_idstr_element(*iptr_))
++iptr_; ++iptr_;
binary_.second.assign(pbegin, iptr_); binary_.second.assign(pbegin, iptr_);
_m_eat_whitespace(); _m_eat_whitespace();
if((iptr_ < endptr_) && (')' == *iptr_)) if ((iptr_ < endptr_) && (')' == *iptr_))
{ {
++iptr_; ++iptr_;
return token::binary; return token::binary;
@ -273,62 +277,64 @@ namespace nana{ namespace widgets{ namespace skeletons
} }
} }
} }
return token::eof; return token::eof;
} }
if(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || '_' == ch)
if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || '_' == ch)
{ {
--iptr_; --iptr_;
//Here is a identifier //Here is a identifier
_m_read_idstr(); _m_read_idstr();
if(L"font" == idstr_) if (L"font" == idstr_)
return token::font; return token::font;
else if(L"bold" == idstr_) else if (L"bold" == idstr_)
return token::bold; return token::bold;
else if(L"size" == idstr_) else if (L"size" == idstr_)
return token::size; return token::size;
else if(L"baseline" == idstr_) else if (L"baseline" == idstr_)
return token::baseline; return token::baseline;
else if(L"top" == idstr_) else if (L"top" == idstr_)
return token::top; return token::top;
else if(L"center" == idstr_) else if (L"center" == idstr_)
return token::center; return token::center;
else if(L"bottom" == idstr_) else if (L"bottom" == idstr_)
return token::bottom; return token::bottom;
else if(L"color" == idstr_) else if (L"color" == idstr_)
return token::color; return token::color;
else if(L"image" == idstr_) else if (L"image" == idstr_)
return token::image; return token::image;
else if(L"true" == idstr_) else if (L"true" == idstr_)
return token::_true; return token::_true;
else if(L"url" == idstr_) else if (L"url" == idstr_)
return token::url; return token::url;
else if(L"target" == idstr_) else if (L"target" == idstr_)
return token::target; return token::target;
else if(L"false" == idstr_) else if (L"false" == idstr_)
return token::_false; return token::_false;
else if(L"red" == idstr_) else if (L"red" == idstr_)
return token::red; return token::red;
else if(L"green" == idstr_) else if (L"green" == idstr_)
return token::green; return token::green;
else if(L"blue" == idstr_) else if (L"blue" == idstr_)
return token::blue; return token::blue;
else if(L"white" == idstr_) else if (L"white" == idstr_)
return token::white; return token::white;
else if(L"black" == idstr_) else if (L"black" == idstr_)
return token::black; return token::black;
else if(L"min_limited" == idstr_) else if (L"min_limited" == idstr_)
return token::min_limited; return token::min_limited;
else if(L"max_limited" == idstr_) else if (L"max_limited" == idstr_)
return token::max_limited; return token::max_limited;
return token::string; return token::string;
} }
if('0' <= ch && ch <= '9') if ('0' <= ch && ch <= '9')
{ {
--iptr_; --iptr_;
_m_read_number(); _m_read_number();
@ -352,8 +358,7 @@ namespace nana{ namespace widgets{ namespace skeletons
do do
{ {
ch = *(++iptr_); ch = *(++iptr_);
} } while (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ('_' == ch) || ('0' <= ch && ch <= '9'));
while(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ('_' == ch) || ('0' <= ch && ch <= '9'));
idstr_.assign(idstr, iptr_); idstr_.assign(idstr, iptr_);
} }
@ -368,18 +373,18 @@ namespace nana{ namespace widgets{ namespace skeletons
idstr_ += ch; idstr_ += ch;
//First check the number whether will be a hex number. //First check the number whether will be a hex number.
if('0' == ch) if ('0' == ch)
{ {
ch = *++iptr_; ch = *++iptr_;
if((!('0' <= ch && ch <= '9')) && (ch != 'x' && ch != 'X')) if ((!('0' <= ch && ch <= '9')) && (ch != 'x' && ch != 'X'))
return; return;
if(ch == 'x' || ch == 'X') if (ch == 'x' || ch == 'X')
{ {
//Here is a hex number //Here is a hex number
idstr_ += 'x'; idstr_ += 'x';
ch = *++iptr_; ch = *++iptr_;
while(('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')) while (('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F'))
{ {
idstr_ += ch; idstr_ += ch;
ch = *++iptr_; ch = *++iptr_;
@ -392,7 +397,7 @@ namespace nana{ namespace widgets{ namespace skeletons
} }
ch = *++iptr_; ch = *++iptr_;
while('0' <= ch && ch <= '9') while ('0' <= ch && ch <= '9')
{ {
idstr_ += ch; idstr_ += ch;
ch = *++iptr_; ch = *++iptr_;
@ -401,9 +406,9 @@ namespace nana{ namespace widgets{ namespace skeletons
void _m_eat_whitespace() void _m_eat_whitespace()
{ {
while(true) while (true)
{ {
switch(*iptr_) switch (*iptr_)
{ {
case ' ': case ' ':
case '\t': case '\t':
@ -641,6 +646,8 @@ namespace nana{ namespace widgets{ namespace skeletons
while(true) while(true)
{ {
token tk = tknizer.read(); token tk = tknizer.read();
if (token::eof == tk)
break;
switch(tk) switch(tk)
{ {
@ -665,12 +672,53 @@ namespace nana{ namespace widgets{ namespace skeletons
if(fstack.size() > 1) if(fstack.size() > 1)
fstack.pop(); fstack.pop();
break; break;
case token::eof:
return;
default: default:
throw std::runtime_error("invalid token"); throw std::runtime_error("invalid token");
} }
} }
//Reorder the sequence of line blocks for RTL languages.
for (auto & ln : lines_)
{
std::wstring str;
//Position only holds the start positions of blocks in a line.
std::vector<std::size_t> position;
for (auto & b : ln)
{
position.push_back(str.size());
str += b.data_ptr->text();
}
std::remove_reference<decltype(ln)>::type dump;
dump.swap(ln);
auto entities = unicode_bidi{}.reorder(str.c_str(), str.size());
for (auto & e : entities)
{
auto pos = e.begin - str.c_str();
auto i = std::find(position.cbegin(), position.cend(), pos);
//If the pos is not a start position, it indicates the block of bidi entity has been inserted into
//the ln container. Because the content of a block may be divided into multiple bidi entities.
if (i == position.cend())
continue;
ln.push_back(dump[i - position.cbegin()]);
auto const endpos = e.end - str.c_str();
//Check whether the next position is belone to current entity.
while (++i != position.cend())
{
if (*i < static_cast<std::size_t>(endpos))
{
ln.push_back(dump[i - position.cbegin()]);
}
else
break;
}
}
}
} }
iterator begin() iterator begin()

View File

@ -300,6 +300,12 @@ namespace nana
return any_cast<T>(&_m_value()); return any_cast<T>(&_m_value());
} }
template<typename T>
T * value_ptr()
{
return any_cast<T>(&_m_value());
}
template<typename T> template<typename T>
const T& value() const const T& value() const
{ {
@ -309,6 +315,15 @@ namespace nana
return *p; return *p;
} }
template<typename T>
T& value()
{
auto p = any_cast<T>(&_m_value());
if (nullptr == p)
throw std::runtime_error("treebox::value<T>() Invalid type of value.");
return *p;
}
template<typename T> template<typename T>
item_proxy & value(T&& t) item_proxy & value(T&& t)
{ {

View File

@ -50,6 +50,7 @@ namespace nana
}; };
std::vector<entity> reorder(const char_type*, std::size_t len); std::vector<entity> reorder(const char_type*, std::size_t len);
static bool is_text_right(const entity&);
private: private:
static unsigned _m_paragraph_level(const char_type * begin, const char_type * end); static unsigned _m_paragraph_level(const char_type * begin, const char_type * end);
@ -72,7 +73,6 @@ namespace nana
std::vector<unicode_bidi::entity> unicode_reorder(const wchar_t* text, std::size_t length); std::vector<unicode_bidi::entity> unicode_reorder(const wchar_t* text, std::size_t length);
bool unicode_wordbreak(wchar_t left, wchar_t right); bool unicode_wordbreak(wchar_t left, wchar_t right);
} }
#include <nana/pop_ignore_diagnostic> #include <nana/pop_ignore_diagnostic>

View File

@ -15,6 +15,7 @@
#include <windows.h> #include <windows.h>
#endif #endif
#include <array> #include <array>
#include <stdexcept>
namespace { namespace {
std::tm localtime() std::tm localtime()

View File

@ -2,6 +2,9 @@
#include <set> #include <set>
#include <nana/deploy.hpp> #include <nana/deploy.hpp>
#include "../paint/truetype.hpp" #include "../paint/truetype.hpp"
#ifdef _nana_std_has_string_view
# include <string_view>
#endif
#ifdef NANA_WINDOWS #ifdef NANA_WINDOWS
@ -146,6 +149,8 @@ IsWindows8OrGreater()
# include "posix/platform_spec.hpp" # include "posix/platform_spec.hpp"
# include <fontconfig/fontconfig.h> # include <fontconfig/fontconfig.h>
# if defined(NANA_USE_XFT) # if defined(NANA_USE_XFT)
# include <nana/unicode_bidi.hpp>
# include "text_reshaping.hpp"
# include <X11/Xft/Xft.h> # include <X11/Xft/Xft.h>
# include <iconv.h> # include <iconv.h>
# include <fstream> # include <fstream>
@ -211,16 +216,37 @@ namespace nana
int const init_x = x; int const init_x = x;
std::unique_ptr<FT_UInt[]> glyph_indexes(new FT_UInt[len]); std::unique_ptr<FT_UInt[]> glyph_indexes(new FT_UInt[len]);
while(true) //The RTL and shaping should be handled manually, because the libXft and X doesn't support these language features.
std::wstring rtl;
auto ents = unicode_reorder(str, len);
for(auto & e : ents)
{ {
auto preferred = _m_scan_fonts(xft, str, len, glyph_indexes.get()); auto size = static_cast<std::size_t>(e.end - e.begin);
x += _m_draw(xftdraw, xftcolor, preferred.first, x, y, str, preferred.second, glyph_indexes.get()); auto p = e.begin;
if(len == preferred.second) if(unicode_bidi::is_text_right(e))
break; {
auto restr = nana::reshaping::arabic::reshape(std::wstring{e.begin, e.end});
rtl.assign(restr.crbegin(), restr.crend());
len -= preferred.second; p = rtl.c_str();
str += preferred.second; size = rtl.size();
}
while(true)
{
//Scan the string until the character which font is not same with the font of the first character where the scan begins.
auto preferred = _m_scan_fonts(xft, p, size, glyph_indexes.get());
x += _m_draw(xftdraw, xftcolor, preferred.first, x, y, p, preferred.second, glyph_indexes.get());
if(size == preferred.second)
break;
size -= preferred.second;
p += preferred.second;
}
rtl.clear();
} }
return x - init_x; return x - init_x;
@ -236,22 +262,28 @@ namespace nana
std::unique_ptr<unsigned[]> pxbuf{new unsigned[len]}; std::unique_ptr<unsigned[]> pxbuf{new unsigned[len]};
auto pbuf = pxbuf.get(); auto pbuf = pxbuf.get();
auto pstr = str;
auto size = len;
while(true) #ifdef _nana_std_has_string_view
{ std::wstring_view s{str, len};
auto preferred = _m_scan_fonts(xft, pstr, size, glyph_indexes.get()); #else
std::wstring s{str, len};
#endif
//Don't reverse the string
_m_reorder_reshaping(s, false, [&,xft, str](const wchar_t* p, std::size_t size, const wchar_t* pstr) mutable{
while(true)
{
auto preferred = _m_scan_fonts(xft, p, size, glyph_indexes.get());
_m_glyph_px(preferred.first, pstr, preferred.second, glyph_indexes.get(), pbuf); _m_glyph_px(preferred.first, p, preferred.second, glyph_indexes.get(), pbuf + (pstr - str));
if(size == preferred.second) if(size == preferred.second)
break; break;
size -= preferred.second; size -= preferred.second;
pstr += preferred.second; p += preferred.second;
pbuf += preferred.second; pstr += preferred.second;
} }
});
return pxbuf; return pxbuf;
} }
@ -265,21 +297,69 @@ namespace nana
std::unique_ptr<FT_UInt[]> glyph_indexes(new FT_UInt[len]); std::unique_ptr<FT_UInt[]> glyph_indexes(new FT_UInt[len]);
while(len > 0) #ifdef _nana_std_has_string_view
{ std::wstring_view s{str, len};
auto preferred = _m_scan_fonts(xft, str, len, glyph_indexes.get()); #else
std::wstring s{str, len};
#endif
//Don't reverse the string
_m_reorder_reshaping(s, false, [&,xft, str](const wchar_t* p, std::size_t size, const wchar_t* /*pstr*/) mutable{
while(true)
{
auto preferred = _m_scan_fonts(xft, p, size, glyph_indexes.get());
extent.width += _m_extents(preferred.first, str, preferred.second, glyph_indexes.get()); extent.width += _m_extents(preferred.first, p, preferred.second, glyph_indexes.get());
if(preferred.first->ascent + preferred.first->descent > static_cast<int>(extent.height)) if(preferred.first->ascent + preferred.first->descent > static_cast<int>(extent.height))
extent.height = preferred.first->ascent + preferred.first->descent; extent.height = preferred.first->ascent + preferred.first->descent;
if(size == preferred.second)
break;
size -= preferred.second;
p += preferred.second;
}
});
len -= preferred.second;
str += preferred.second;
}
return extent; return extent;
} }
private: private:
/// @param reverse Indicates whether to reverse the string, it only reverse the RTL language string.
template<typename Function>
#ifdef _nana_std_has_string_view
void _m_reorder_reshaping(std::wstring_view str, bool reverse, Function fn)
#else
void _m_reorder_reshaping(const std::wstring& str, bool reverse, Function fn)
#endif
{
//The RTL and shaping should be handled manually, because the libXft and X doesn't support these language features.
std::wstring rtl;
auto ents = unicode_reorder(str.data(), str.size());
for(auto & e : ents)
{
auto size = static_cast<std::size_t>(e.end - e.begin);
auto p = e.begin;
if(unicode_bidi::is_text_right(e))
{
//Reshape the str
auto restr = nana::reshaping::arabic::reshape(std::wstring{e.begin, e.end});
if(reverse)
rtl.assign(restr.crbegin(), restr.crend());
else
rtl.swap(restr);
p = rtl.c_str();
size = rtl.size();
}
fn(p, size, e.begin);
rtl.clear();
}
}
//Tab is a invisible character //Tab is a invisible character
int _m_draw(::XftDraw* xftdraw, ::XftColor* xftcolor, ::XftFont* xft, int x, int y, const wchar_t* str, std::size_t len, const FT_UInt* glyph_indexes) int _m_draw(::XftDraw* xftdraw, ::XftColor* xftcolor, ::XftFont* xft, int x, int y, const wchar_t* str, std::size_t len, const FT_UInt* glyph_indexes)
{ {
@ -298,14 +378,14 @@ namespace nana
if(ptab == p) if(ptab == p)
{ {
++p; ++p;
//x += static_cast<int>(tab_pixels_);
continue; continue;
} }
auto const size = ptab - p; auto const size = ptab - p;
::XftDrawGlyphs(xftdraw, xftcolor, xft, x, y, glyph_indexes + off, size); ::XftDrawGlyphs(xftdraw, xftcolor, xft, x, y, glyph_indexes + off, size);
::XftGlyphExtents(disp_, xft, glyph_indexes + off, size, &ext); ::XftGlyphExtents(disp_, xft, glyph_indexes + off, size, &ext);
x += ext.xOff; x += ext.xOff;
if(ptab == end) if(ptab == end)
@ -359,10 +439,10 @@ namespace nana
if('\t' != *p) if('\t' != *p)
{ {
::XftGlyphExtents(disp_, xft, glyph_indexes, 1, &extent); ::XftGlyphExtents(disp_, xft, glyph_indexes, 1, &extent);
*pxbuf = extent.xOff; *pxbuf++ = extent.xOff;
} }
else else
*pxbuf = 0;//tab_pixels_; *pxbuf++ = 0;//tab_pixels_;
++glyph_indexes; ++glyph_indexes;
} }

View File

@ -0,0 +1,297 @@
namespace nana
{
namespace reshaping
{
namespace arabic
{
const unsigned short TATWEEL = 0x0640;
const unsigned short ZWJ = 0x200D;
const int unshaped = 255;
const int isolated = 0;
const int initial = 1;
const int medial = 2;
const int final = 3;
unsigned short letters[][4] = {
{0xFE80, 0, 0, 0}, //ARABIC LETTER HAMZA
{0xFE81, 0, 0, 0xFE82}, //ARABIC LETTER ALEF WITH MADDA ABOVE
{0xFE83, 0, 0, 0xFE84}, //ARABIC LETTER ALEF WITH HAMZA ABOVE
{0xFE85, 0, 0, 0xFE86}, //ARABIC LETTER WAW WITH HAMZA ABOVE
{0xFE87, 0, 0, 0xFE88}, //ARABIC LETTER ALEF WITH HAMZA BELOW
{0xFE89, 0xFE8B, 0xFE8C, 0xFE8A}, //ARABIC LETTER YEH WITH HAMZA ABOVE
{0xFE8D, 0, 0, 0xFE8E}, //ARABIC LETTER ALEF
{0xFE8F, 0xFE91, 0xFE92, 0xFE90}, //ARABIC LETTER BEH
{0xFE93, 0, 0, 0xFE94}, //ARABIC LETTER TEH MARBUTA
{0xFE95, 0xFE97, 0xFE98, 0xFE96}, //ARABIC LETTER TEH
{0xFE99, 0xFE9B, 0xFE9C, 0xFE9A}, //ARABIC LETTER THEH
{0xFE9D, 0xFE9F, 0xFEA0, 0xFE9E}, //ARABIC LETTER JEEM
{0xFEA1, 0xFEA3, 0xFEA4, 0xFEA2}, //ARABIC LETTER HAH
{0xFEA5, 0xFEA7, 0xFEA8, 0xFEA6}, //ARABIC LETTER KHAH
{0xFEA9, 0, 0, 0xFEAA}, //ARABIC LETTER DAL
{0xFEAB, 0, 0, 0xFEAC}, //ARABIC LETTER THAL
{0xFEAD, 0, 0, 0xFEAE}, //ARABIC LETTER REH
{0xFEAF, 0, 0, 0xFEB0}, //ARABIC LETTER ZAIN
{0xFEB1, 0xFEB3, 0xFEB4, 0xFEB2}, //ARABIC LETTER SEEN
{0xFEB5, 0xFEB7, 0xFEB8, 0xFEB6}, //ARABIC LETTER SHEEN
{0xFEB9, 0xFEBB, 0xFEBC, 0xFEBA}, //ARABIC LETTER SAD
{0xFEBD, 0xFEBF, 0xFEC0, 0xFEBE}, //ARABIC LETTER DAD
{0xFEC1, 0xFEC3, 0xFEC4, 0xFEC2}, //ARABIC LETTER TAH
{0xFEC5, 0xFEC7, 0xFEC8, 0xFEC6}, //ARABIC LETTER ZAH
{0xFEC9, 0xFECB, 0xFECC, 0xFECA}, //ARABIC LETTER AIN
{0xFECD, 0xFECF, 0xFED0, 0xFECE}, //ARABIC LETTER GHAIN
{TATWEEL, TATWEEL, TATWEEL, TATWEEL}, //ARABIC TATWEEL
{0xFED1, 0xFED3, 0xFED4, 0xFED2}, //ARABIC LETTER FEH
{0xFED5, 0xFED7, 0xFED8, 0xFED6}, //ARABIC LETTER QAF
{0xFED9, 0xFEDB, 0xFEDC, 0xFEDA}, //ARABIC LETTER KAF
{0xFEDD, 0xFEDF, 0xFEE0, 0xFEDE}, //ARABIC LETTER LAM
{0xFEE1, 0xFEE3, 0xFEE4, 0xFEE2}, //ARABIC LETTER MEEM
{0xFEE5, 0xFEE7, 0xFEE8, 0xFEE6}, //ARABIC LETTER NOON
{0xFEE9, 0xFEEB, 0xFEEC, 0xFEEA}, //ARABIC LETTER HEH
{0xFEED, 0, 0, 0xFEEE}, //ARABIC LETTER WAW
{0xFEEF, 0xFBE8, 0xFBE9, 0xFEF0}, //ARABIC LETTER (UIGHUR KAZAKH KIRGHIZ)? ALEF MAKSURA
{0xFEF1, 0xFEF3, 0xFEF4, 0xFEF2}, //ARABIC LETTER YEH
{0xFB50, 0, 0, 0xFB51}, //ARABIC LETTER ALEF WASLA
{0xFBDD, 0, 0, 0}, //ARABIC LETTER U WITH HAMZA ABOVE
{0xFB66, 0xFB68, 0xFB69, 0xFB67}, //ARABIC LETTER TTEH
{0xFB5E, 0xFB60, 0xFB61, 0xFB5F}, //ARABIC LETTER TTEHEH
{0xFB52, 0xFB54, 0xFB55, 0xFB53}, //ARABIC LETTER BEEH
{0xFB56, 0xFB58, 0xFB59, 0xFB57}, //ARABIC LETTER PEH
{0xFB62, 0xFB64, 0xFB65, 0xFB63}, //ARABIC LETTER TEHEH
{0xFB5A, 0xFB5C, 0xFB5D, 0xFB5B}, //ARABIC LETTER BEHEH
{0xFB76, 0xFB78, 0xFB79, 0xFB77}, //ARABIC LETTER NYEH
{0xFB72, 0xFB74, 0xFB75, 0xFB73}, //ARABIC LETTER DYEH
{0xFB7A, 0xFB7C, 0xFB7D, 0xFB7B}, //ARABIC LETTER TCHEH
{0xFB7E, 0xFB80, 0xFB81, 0xFB7F}, //ARABIC LETTER TCHEHEH
{0xFB88, 0, 0, 0xFB89}, //ARABIC LETTER DDAL
{0xFB84, 0, 0, 0xFB85}, //ARABIC LETTER DAHAL
{0xFB82, 0, 0, 0xFB83}, //ARABIC LETTER DDAHAL
{0xFB86, 0, 0, 0xFB87}, //ARABIC LETTER DUL
{0xFB8C, 0, 0, 0xFB8D}, //ARABIC LETTER RREH
{0xFB8A, 0, 0, 0xFB8B}, //ARABIC LETTER JEH
{0xFB6A, 0xFB6C, 0xFB6D, 0xFB6B}, //ARABIC LETTER VEH
{0xFB6E, 0xFB70, 0xFB71, 0xFB6F}, //ARABIC LETTER PEHEH
{0xFB8E, 0xFB90, 0xFB91, 0xFB8F}, //ARABIC LETTER KEHEH
{0xFBD3, 0xFBD5, 0xFBD6, 0xFBD4}, //ARABIC LETTER NG
{0xFB92, 0xFB94, 0xFB95, 0xFB93}, //ARABIC LETTER GAF
{0xFB9A, 0xFB9C, 0xFB9D, 0xFB9B}, //ARABIC LETTER NGOEH
{0xFB96, 0xFB98, 0xFB99, 0xFB97}, //ARABIC LETTER GUEH
{0xFB9E, 0, 0, 0xFB9F}, //ARABIC LETTER NOON GHUNNA
{0xFBA0, 0xFBA2, 0xFBA3, 0xFBA1}, //ARABIC LETTER RNOON
{0xFBAA, 0xFBAC, 0xFBAD, 0xFBAB}, //ARABIC LETTER HEH DOACHASHMEE
{0xFBA4, 0, 0, 0xFBA5}, //ARABIC LETTER HEH WITH YEH ABOVE
{0xFBA6, 0xFBA8, 0xFBA9, 0xFBA7}, //ARABIC LETTER HEH GOAL
{0xFBE0, 0, 0, 0xFBE1}, //ARABIC LETTER KIRGHIZ OE
{0xFBD9, 0, 0, 0xFBDA}, //ARABIC LETTER OE
{0xFBD7, 0, 0, 0xFBD8}, //ARABIC LETTER U
{0xFBDB, 0, 0, 0xFBDC}, //ARABIC LETTER YU
{0xFBE2, 0, 0, 0xFBE3}, //ARABIC LETTER KIRGHIZ YU
{0xFBDE, 0, 0, 0xFBDF}, //ARABIC LETTER VE
{0xFBFC, 0xFBFE, 0xFBFF, 0xFBFD}, //ARABIC LETTER FARSI YEH
{0xFBE4, 0xFBE6, 0xFBE7, 0xFBE5}, //ARABIC LETTER E
{0xFBAE, 0, 0, 0xFBAF}, //ARABIC LETTER YEH BARREE
{0xFBB0, 0, 0, 0xFBB1}, //ARABIC LETTER YEH BARREE WITH HAMZA ABOVE
{ZWJ, ZWJ, ZWJ, ZWJ}
};
bool harakat(wchar_t letter)
{
return
(0x0610 <= letter && letter <= 0x061A) ||
(0x064B <= letter && letter <= 0x065F) ||
(0x0670 == letter) ||
(0x06D6 <= letter && letter <= 0x06DC) ||
(0x06DF <= letter && letter <= 0x06E8) ||
(0x06EA <= letter && letter <= 0x06ED) ||
(0x08D4 <= letter && letter <= 0x08E1) ||
(0x08D4 <= letter && letter <= 0x08ED) ||
(0x08E3 <= letter && letter <= 0x08FF);
}
int form_index(wchar_t letter)
{
static unsigned short ranges[][2]={
{0x0621, 0x063A},
{0x0640, 0x064A},
{0x0671, 0x0671},
{0x0677, 0x0677},
{0x0679, 0x067B},
{0x067E, 0x0680},
{0x0683, 0x0684},
{0x0686, 0x0688},
{0x068C, 0x068E},
{0x0691, 0x0691},
{0x0698, 0x0698},
{0x06A4, 0x06A4},
{0x06A6, 0x06A6},
{0x06A9, 0x06A9},
{0x06AD, 0x06AD},
{0x06AF, 0x06AF},
{0x06B1, 0x06B1},
{0x06B3, 0x06B3},
{0x06BA, 0x06BB},
{0x06BE, 0x06BE},
{0x06C0, 0x06C1},
{0x06C5, 0x06C9},
{0x06CB, 0x06CC},
{0x06D0, 0x06D0},
{0x06D2, 0x06D3},
{ZWJ, ZWJ}
};
if((letter < 0x0621) || (0x06D3 < letter && letter != ZWJ))
return -1;
int base = 0;
for(std::size_t i = 0; i < sizeof(ranges) / sizeof(unsigned short) / 2; ++i)
{
if(ranges[i][0] <= letter && letter <= ranges[i][1])
return static_cast<int>(letter - ranges[i][0]) + base;
base += static_cast<int>(ranges[i][1] - ranges[i][0]) + 1;
}
return base;
}
wchar_t connect_before(wchar_t letter)
{
auto idx = form_index(letter);
if(idx < 0)
return 0;
return letters[idx][final] || letters[idx][medial];
}
wchar_t connect_after(wchar_t letter)
{
auto idx = form_index(letter);
if(idx < 0)
return 0;
return letters[idx][initial] || letters[idx][medial];
}
wchar_t connect_before_after(wchar_t letter)
{
auto idx = form_index(letter);
if(idx < 0)
return 0;
return letters[idx][medial];
}
std::wstring reshape(const std::wstring& text)
{
bool const use_unshaped_instead_of_isolated = false;
bool const delete_harakat = true;
bool const shift_harakat_position = false;
bool const delete_tatweel = false;
bool const support_zwj = true;
const int no_form = -1;
const int isolated_form = use_unshaped_instead_of_isolated ? unshaped : isolated;
std::wstring output;
std::vector<int> forms;
std::map<int, std::wstring> positions_harakat;
for(auto letter: text)
{
if(harakat(letter))
{
if(!delete_harakat)
{
int position = static_cast<int>(output.size()) - 1;
if (shift_harakat_position)
--position;
if (positions_harakat.count(position) == 0)
positions_harakat[position];
if (shift_harakat_position)
{
auto & ph = positions_harakat[position];
ph.insert(ph.cbegin(), letter);
}
else
positions_harakat[position] += letter;
}
continue;
}
else if(((TATWEEL == letter) && delete_tatweel) || (ZWJ == letter && !support_zwj))
{
continue;
}
auto idx = form_index(letter);
if(idx < 0)
{
output += letter;
forms.push_back(no_form);
continue;
}
if(forms.empty())
{
output += letter;
forms.push_back(isolated_form);
continue;
}
if((forms.back() == no_form) || (!connect_before(letter)) || (!connect_after(output.back())) ||
((forms.back() == final) && !connect_before_after(output.back())))
{
output += letter;
forms.push_back(isolated_form);
}
else if(forms.back() == isolated_form)
{
forms.back() = initial;
output += letter;
forms.push_back(final);
}
else
{
forms.back() = medial;
output += letter;
forms.push_back(final);
}
//Remove ZWJ if it's the second to last item as it won't be useful
if(support_zwj && (output.size() > 1) && (output[output.size() - 2] == ZWJ))
output.erase(output.size() - 2, 1);
}
//Remove ZWJ if it's the second to last item as it won't be useful
if(support_zwj && (output.size() > 0) && (output.back() == ZWJ))
output.pop_back();
std::wstring result;
if((!delete_harakat) && positions_harakat.count(-1))
result += positions_harakat[-1];
for(std::size_t i = 0; i < output.size(); ++i)
{
if(output[i])
{
if(forms[i] == no_form || forms[i] == unshaped)
result += output[i];
else
result += letters[form_index(output[i])][forms[i]];
}
if(!delete_harakat)
if(positions_harakat.count(i))
result += positions_harakat[i];
}
return result;
}
}//end namespace arabic
}//end namespace reshaping
}

View File

@ -1,7 +1,7 @@
/** /**
* A Bedrock Implementation * A Bedrock Implementation
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -1191,9 +1191,7 @@ namespace detail
} }
else if (pointer_wd != root_window) else if (pointer_wd != root_window)
{ {
DWORD pid = 0; if (::GetWindowThreadProcessId(pointer_wd, nullptr) != ::GetCurrentThreadId())
::GetWindowThreadProcessId(pointer_wd, &pid);
if (pid == ::GetCurrentProcessId())
::PostMessage(pointer_wd, message, wParam, lParam); ::PostMessage(pointer_wd, message, wParam, lParam);
} }
} }

View File

@ -1,7 +1,7 @@
/** /**
* An Implementation of Place for Layout * An Implementation of Place for Layout
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE or copy at * (See accompanying file LICENSE or copy at
@ -1810,6 +1810,12 @@ namespace nana
{ {
grabbed_ = false; grabbed_ = false;
this->_m_update_div(impl_->div_text); this->_m_update_div(impl_->div_text);
//revise the position of splitter window.(#512)
//when the splitter is dragged, the place recalculates the left/right fields the weight in percentage, then update
//position of the splitter field. It may cause deviation that new splitter field position is not same with the position of
//splitter window after dragging a bit, because the field position is calcuated with left/right fields's weights which are float-point values.
splitter_.move(this->field_area);
} }
else if (event_code::mouse_move == arg.evt_code) else if (event_code::mouse_move == arg.evt_code)
{ {

View File

@ -1,7 +1,7 @@
/* /*
* A List Box Implementation * A List Box Implementation
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -5736,6 +5736,14 @@ namespace nana
return index_pair{ npos, npos }; return index_pair{ npos, npos };
} }
listbox::index_pair listbox::index_cast(index_pair idx, bool from_display_order) const
{
internal_scope_guard lock;
idx.item = at(idx.cat).index_cast(idx.item, from_display_order);
return idx;
}
listbox::index_pair listbox::hovered(bool return_end) const listbox::index_pair listbox::hovered(bool return_end) const
{ {
using parts = drawerbase::listbox::essence::parts; using parts = drawerbase::listbox::essence::parts;
@ -5755,9 +5763,11 @@ namespace nana
if (0 < pos.cat) if (0 < pos.cat)
--pos.cat; --pos.cat;
pos.item = this->size_item(pos.cat); pos.item = this->size_item(pos.cat);
}
return pos;
return pos;
}
return index_cast(pos, true);
} }
else if (return_end) else if (return_end)
return index_pair{ this->size_categ() - 1, this->size_item(this->size_categ() - 1) }; return index_pair{ this->size_categ() - 1, this->size_item(this->size_categ() - 1) };

View File

@ -1,7 +1,7 @@
/* /*
* A text editor implementation * A text editor implementation
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -2482,10 +2482,8 @@ namespace nana {
if (coord != coord_org) if (coord != coord_org)
{ {
auto pos_x = pos.x;
impl_->cview->move_origin(origin - impl_->cview->origin()); impl_->cview->move_origin(origin - impl_->cview->origin());
pos = _m_coordinate_to_caret(coord, false); pos = _m_coordinate_to_caret(coord, false);
pos.x = pos_x;
} }
if (pos != points_.caret) { if (pos != points_.caret) {
@ -3485,9 +3483,14 @@ namespace nana {
void rtl_string(point strpos, const wchar_t* str, std::size_t len, std::size_t str_px, unsigned glyph_front, unsigned glyph_selected, bool has_focused) void rtl_string(point strpos, const wchar_t* str, std::size_t len, std::size_t str_px, unsigned glyph_front, unsigned glyph_selected, bool has_focused)
{ {
editor_._m_draw_parse_string(parser_, true, strpos, selection_color(true, has_focused), str, len); //Draw twices for RTL language in order to avoid character transforming.
//one is to draw whole string as unselected, another one is to draw whole string as selected.
//Draw as unselected
editor_._m_draw_parse_string(parser_, true, strpos, editor_.scheme_->foreground, str, len);
//Draw selected part
//Draw as selected, and copy the selected part to the graph.
paint::graphics graph({ glyph_selected, line_px_ }); paint::graphics graph({ glyph_selected, line_px_ });
graph.typeface(this->graph_.typeface()); graph.typeface(this->graph_.typeface());
graph.rectangle(true, selection_color(false, has_focused)); graph.rectangle(true, selection_color(false, has_focused));

View File

@ -1736,11 +1736,11 @@ namespace nana
//class trigger //class trigger
//struct treebox_node_type //struct treebox_node_type
trigger::treebox_node_type::treebox_node_type() trigger::treebox_node_type::treebox_node_type()
:expanded(false), checked(checkstate::unchecked), hidden(false) :expanded(false), hidden(false), checked(checkstate::unchecked)
{} {}
trigger::treebox_node_type::treebox_node_type(std::string text) trigger::treebox_node_type::treebox_node_type(std::string text)
:text(std::move(text)), expanded(false), checked(checkstate::unchecked), hidden(false) :text(std::move(text)), expanded(false), hidden(false), checked(checkstate::unchecked)
{} {}
trigger::treebox_node_type& trigger::treebox_node_type::operator=(const treebox_node_type& rhs) trigger::treebox_node_type& trigger::treebox_node_type::operator=(const treebox_node_type& rhs)

View File

@ -1,7 +1,7 @@
/* /*
* Bitmap Format Graphics Implementation * Bitmap Format Graphics Implementation
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -14,6 +14,7 @@
#define NANA_PAINT_DETAIL_IMAGE_BMP_HPP #define NANA_PAINT_DETAIL_IMAGE_BMP_HPP
#include <memory> #include <memory>
#include <cstring>
#include "image_pixbuf.hpp" #include "image_pixbuf.hpp"
namespace nana{ namespace paint namespace nana{ namespace paint
@ -106,7 +107,7 @@ namespace nana{ namespace paint
if (16 <= header->biBitCount) if (16 <= header->biBitCount)
pixbuf_.put(bits, header->biWidth, bmp_height, header->biBitCount, bytes_per_line, (header->biHeight < 0)); pixbuf_.put(bits, header->biWidth, bmp_height, header->biBitCount, bytes_per_line, (header->biHeight < 0));
else else
_m_put_with_palette(header, bits, bytes_per_line); _m_put_with_palette(header, bits, bmp_file->bfSize - bmp_file->bfOffBits, bytes_per_line);
return true; return true;
} }
@ -132,7 +133,72 @@ namespace nana{ namespace paint
return false; return false;
} }
private: private:
void _m_put_with_palette(const bitmap_info_header* header, const unsigned char* pixel_indexes, unsigned line_bytes) std::unique_ptr<unsigned char[]> _m_decompress_rle8(const bitmap_info_header* header, const unsigned char* data, std::size_t data_size)
{
std::size_t const lines = std::abs(header->biHeight);
unsigned char* const indexes = new unsigned char[header->biWidth * lines];
auto p = indexes;
auto p_line = p;
auto const p_end = indexes + header->biWidth * lines;
std::size_t line_pos = 0;
auto end = data + data_size;
while (data != end && p < p_end)
{
if (0 == data[0])
{
//escape
if (0 == data[1])
{
//eol
data += 2;
++line_pos;
}
else if (1 == data[1])
{
//eof
data += 2;
break;
}
else if (2 == data[1])
{
//delta
auto x = data[2];
auto y = data[3];
// Check if the delta is available
if ((p + x < p_line + header->biWidth) && (line_pos + y < lines))
{
p += y * header->biWidth + x;
line_pos += y;
}
else
break;
data += 4;
}
else
{
//absolute
std::memcpy(p, data + 2, data[1]);
p += data[1];
data += ((data[1] + 1) & 0xFFE) + 2;
}
}
else
{
std::memset(p, data[1], data[0]);
p += data[0];
data += 2;
}
}
return std::unique_ptr<unsigned char[]>{ indexes };
}
void _m_put_with_palette(const bitmap_info_header* header, const unsigned char* bits, std::size_t length, unsigned line_bytes)
{ {
auto const image_height = std::abs(header->biHeight); auto const image_height = std::abs(header->biHeight);
const std::size_t total_pixels = header->biWidth * static_cast<std::size_t>(image_height); const std::size_t total_pixels = header->biWidth * static_cast<std::size_t>(image_height);
@ -152,9 +218,19 @@ namespace nana{ namespace paint
if (8 == header->biBitCount) if (8 == header->biBitCount)
{ {
//decompressed indexes
std::unique_ptr<unsigned char[]> indexes;
if (1 == header->biCompression)
{
indexes = _m_decompress_rle8(header, bits, length);
line_bytes = header->biWidth;
bits = indexes.get();
}
while (dst_px < end_dst_px) while (dst_px < end_dst_px)
{ {
auto px_indexes = pixel_indexes + line_bytes * line_pos; auto px_indexes = bits + line_bytes * line_pos;
auto const line_end_dst_px = dst_px + header->biWidth; auto const line_end_dst_px = dst_px + header->biWidth;
while (dst_px != line_end_dst_px) while (dst_px != line_end_dst_px)
{ {
@ -173,7 +249,7 @@ namespace nana{ namespace paint
{ {
while (dst_px < end_dst_px) while (dst_px < end_dst_px)
{ {
auto px_indexes = pixel_indexes + line_bytes * line_pos; auto px_indexes = bits + line_bytes * line_pos;
auto const line_end_dst_px = dst_px + header->biWidth; auto const line_end_dst_px = dst_px + header->biWidth;
std::size_t pos = 0; std::size_t pos = 0;
switch (header->biBitCount) switch (header->biBitCount)

View File

@ -1,7 +1,7 @@
/** /**
* Paint Image Implementation * Paint Image Implementation
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -108,155 +108,72 @@ namespace paint
return *this; return *this;
} }
std::shared_ptr<image::image_impl_interface> create_image(const fs::path & p) // Check file type through file format signature
std::shared_ptr<image::image_impl_interface> create_image(const char* buf, std::size_t len)
{ {
std::shared_ptr<image::image_impl_interface> ptr; if (buf && len >= 8)
auto ext = p.extension().native();
if (ext.empty())
return ptr;
std::transform(ext.begin(), ext.end(), ext.begin(), [](int ch)
{ {
if ('A' <= ch && ch <= 'Z') if (std::strncmp("\x00\x00\x01\x00", buf, 4) == 0)
ch -= ('A' - 'a');
return ch;
});
#if defined(NANA_WINDOWS)
const wchar_t* ext_ico = L".ico";
const wchar_t* ext_png = L".png";
const wchar_t* ext_jpg = L".jpg";
const wchar_t* ext_jpeg = L".jpeg";
#else
const char* ext_ico = ".ico";
const char* ext_png = ".png";
const char* ext_jpg = ".jpg";
const char* ext_jpeg = ".jpeg";
#endif
do
{
if (ext_ico == ext)
{ {
ptr = std::make_shared<detail::image_ico>(); return std::make_shared<detail::image_ico>();
break;
} }
else if (std::strncmp(buf, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == 0)
if (ext_png == ext)
{ {
#if defined(NANA_ENABLE_PNG) #if defined(NANA_ENABLE_PNG)
ptr = std::make_shared<detail::image_png>(); return std::make_shared<detail::image_png>();
#else
return ptr;
#endif #endif
break;
} }
else if (std::strncmp("\xFF\xD8\xFF", buf, 3) == 0)
if (ext_jpg == ext || ext_jpeg == ext)
{ {
#if defined(NANA_ENABLE_JPEG) #if defined(NANA_ENABLE_JPEG)
ptr = std::make_shared<detail::image_jpeg>(); return std::make_shared<detail::image_jpeg>();
#else
return ptr;
#endif #endif
break;
} }
} while (false); else if (*reinterpret_cast<const short*>("BM") == *reinterpret_cast<const short*>(buf))
return std::make_shared<detail::image_bmp>();
else if (*reinterpret_cast<const short*>("MZ") == *reinterpret_cast<const short*>(buf))
return std::make_shared<detail::image_ico_resource>();
}
//Check for BMP return nullptr;
if (!ptr)
{
#ifndef NANA_MINGW
std::ifstream ifs(p.c_str(), std::ios::binary);
#else
std::ifstream ifs(to_osmbstr(to_utf8(p.native())).c_str(), std::ios::binary);
#endif
if (ifs)
{
unsigned short meta = 0;
ifs.read(reinterpret_cast<char*>(&meta), 2);
if (*reinterpret_cast<const short*>("BM") == meta)
ptr = std::make_shared<detail::image_bmp>();
else if (*reinterpret_cast<const short*>("MZ") == meta)
ptr = std::make_shared<detail::image_ico_resource>();
}
} }
return ptr; bool image::open(const ::std::string& img)
{
fs::path p(img);
image_ptr_.reset();
std::ifstream file{ p, std::ios::binary };
if (file)
{
char buf[8];
if (file.read(buf, 8).gcount() == 8)
image_ptr_ = create_image(buf, 8);
}
return (image_ptr_ ? image_ptr_->open(p) : false);
} }
bool image::open(const ::std::string& file) bool image::open(const std::wstring& img)
{ {
fs::path path(file); fs::path p(img);
image_ptr_ = create_image(path); image_ptr_.reset();
return (image_ptr_ ? image_ptr_->open(path) : false);
}
bool image::open(const std::wstring& file) std::ifstream file{ p, std::ios::binary };
{ if (file)
fs::path path(file); {
image_ptr_ = create_image(path); char buf[8];
return (image_ptr_ ? image_ptr_->open(path) : false); if (file.read(buf, 8).gcount() == 8)
image_ptr_ = create_image(buf, 8);
}
return (image_ptr_ ? image_ptr_->open(p) : false);
} }
bool image::open(const void* data, std::size_t bytes) bool image::open(const void* data, std::size_t bytes)
{ {
close(); image_ptr_ = create_image(static_cast<const char*>(data), bytes);
return (image_ptr_ ? image_ptr_->open(data, bytes) : false);
if (bytes > 2)
{
std::shared_ptr<image::image_impl_interface> ptr;
auto meta = *reinterpret_cast<const unsigned short*>(data);
if (*reinterpret_cast<const short*>("BM") == meta)
ptr = std::make_shared<detail::image_bmp>();
else if (*reinterpret_cast<const short*>("MZ") == meta)
ptr = std::make_shared<detail::image_ico_resource>();
else
{
if (bytes > 8 && (0x474e5089 == *reinterpret_cast<const unsigned*>(data)))
{
#if defined(NANA_ENABLE_PNG)
ptr = std::make_shared<detail::image_png>();
#endif
}
else
{
#if defined(NANA_ENABLE_JPEG)
if ((bytes > 11) && (0xd8ff == *reinterpret_cast<const unsigned short*>(data)))
{
switch(*reinterpret_cast<const unsigned*>(reinterpret_cast<const char*>(data)+6))
{
case 0x4649464A: //JFIF
case 0x66697845: //Exif
ptr = std::make_shared<detail::image_jpeg>();
}
}
else
#endif
if ((!ptr) && (bytes > 40))
{
switch (*reinterpret_cast<const unsigned*>(data))
{
case 40:
case 0x00010000:
if (!ptr && bytes > 40)
ptr = std::make_shared<detail::image_ico>();
}
}
}
}
if (ptr)
{
image_ptr_.swap(ptr);
return image_ptr_->open(data, bytes);
}
}
return false;
} }

View File

@ -1,7 +1,7 @@
/* /*
* Unicode Bidi-Language Implementation * Unicode Bidi-Language Implementation
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -606,6 +606,11 @@ namespace nana
return reordered; return reordered;
} }
bool unicode_bidi::is_text_right(const entity& e)
{
return ((e.bidi_char_type != unicode_bidi::bidi_char::L) && (e.level & 1));
}
unsigned unicode_bidi::_m_paragraph_level(const char_type * begin, const char_type * end) unsigned unicode_bidi::_m_paragraph_level(const char_type * begin, const char_type * end)
{ {
for(const char_type* i = begin; i != end; ++i) for(const char_type* i = begin; i != end; ++i)
@ -990,7 +995,13 @@ namespace nana
return unicode_character_type::katakana; return unicode_character_type::katakana;
if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || (0x00AA == ch || 0x00B5 == ch || 0x00BA == ch) || (0x00C0 <= ch && ch <= 0x00D6) || if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || (0x00AA == ch || 0x00B5 == ch || 0x00BA == ch) || (0x00C0 <= ch && ch <= 0x00D6) ||
(0x00D8 <= ch && ch <= 0x00F6) || (0x00F8 <= ch && ch <= 0x0236) || (0x0250 <= ch && ch <= 0x02C1)) (0x00D8 <= ch && ch <= 0x00F6) || (0x00F8 <= ch && ch <= 0x0236) || (0x0250 <= ch && ch <= 0x02C1) ||
//Hebrew
(0x05BB <= ch && ch <= 0x05BD) || (0x05BF == ch) || ((0x05C1 <= ch && ch <= 0x05C4) && (0x05C3 != ch)) || (0x05D0 <= ch && ch <= 0x05EA) || (0x05F0 <= ch && ch <= 0x05F3) ||
//Arabic
(0x0610 <= ch && ch <= 0x0615) || (0x0621 <= ch && ch <= 0x063A) || (0x0640 <= ch && ch <= 0x0657) || (0x066E <= ch && ch <= 0x06D3) || (0x06D5 <= ch && ch <= 0x06DC) || (0x06E1 <= ch && ch <= 0x06E8) ||
(0x06ED == ch || 0x06EF == ch) || (0x06FA <= ch && ch <= 0x06FC) || (0x06FF == ch)
)
return unicode_character_type::aletter; return unicode_character_type::aletter;
if ('\'' == ch || 0x00AD == ch || 0x00B7 == ch || 0x05F4 == ch || 0x2019 == ch || 0x2027 == ch) if ('\'' == ch || 0x00AD == ch || 0x00B7 == ch || 0x05F4 == ch || 0x2019 == ch || 0x2027 == ch)
@ -1025,7 +1036,7 @@ namespace nana
return !(unicode_character_type::format == r_type) || (unicode_character_type::katakana == r_type); return !(unicode_character_type::format == r_type) || (unicode_character_type::katakana == r_type);
case unicode_character_type::aletter: case unicode_character_type::aletter:
case unicode_character_type::numeric: case unicode_character_type::numeric:
return !(unicode_character_type::format == r_type) || (unicode_character_type::aletter == r_type) || (unicode_character_type::numeric == r_type); return !((unicode_character_type::format == r_type) || (unicode_character_type::aletter == r_type) || (unicode_character_type::numeric == r_type));
} }
return true; return true;
} }