From 75d760266aba1c4ad0594cbc2fdb29fcc37c118b Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 13 Feb 2020 11:56:35 +0800 Subject: [PATCH 01/21] fix issue scrolling in nested_form causes freezing(#505) --- source/gui/detail/bedrock_windows.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index 58044bb9..a7ccb6c4 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -1,7 +1,7 @@ /** * A Bedrock Implementation * 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. * (See accompanying file LICENSE_1_0.txt or copy at @@ -1191,9 +1191,7 @@ namespace detail } else if (pointer_wd != root_window) { - DWORD pid = 0; - ::GetWindowThreadProcessId(pointer_wd, &pid); - if (pid == ::GetCurrentProcessId()) + if (::GetWindowThreadProcessId(pointer_wd, nullptr) != ::GetCurrentThreadId()) ::PostMessage(pointer_wd, message, wParam, lParam); } } From f26d3d0d6cddd0bfcdd3ed4b4bccc27e543e217a Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 13 Feb 2020 15:01:19 +0800 Subject: [PATCH 02/21] fix issue that wrong caret position in textbox when moving up/down --- source/gui/widgets/skeletons/text_editor.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index 7d8c4475..d669c806 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -1,7 +1,7 @@ /* * A text editor implementation * 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. * (See accompanying file LICENSE_1_0.txt or copy at @@ -2482,10 +2482,8 @@ namespace nana { if (coord != coord_org) { - auto pos_x = pos.x; impl_->cview->move_origin(origin - impl_->cview->origin()); pos = _m_coordinate_to_caret(coord, false); - pos.x = pos_x; } if (pos != points_.caret) { From b41e9ee913fd52fd73a8c09bb5f39679c8794076 Mon Sep 17 00:00:00 2001 From: wangli28 Date: Mon, 17 Feb 2020 10:44:37 +0000 Subject: [PATCH 03/21] Include in datetime.cpp --- source/datetime.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/source/datetime.cpp b/source/datetime.cpp index e6e8fa5b..76e31c1a 100644 --- a/source/datetime.cpp +++ b/source/datetime.cpp @@ -15,6 +15,7 @@ #include #endif #include +#include namespace { std::tm localtime() From 67d4e4376aaafaf666c14404f60e3d27c1167974 Mon Sep 17 00:00:00 2001 From: mixael Date: Mon, 17 Feb 2020 03:33:23 +0300 Subject: [PATCH 04/21] Added option to enable nana config --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e9a9ee48..ce1fdd28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,10 +114,13 @@ endforeach() ### Some nana compilation options ### 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_CONF "enable config.hpp" OFF) ######## 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) target_compile_definitions(nana PUBLIC NANA_AUTOMATIC_GUI_TESTING) # todo: enable_testing() # ?? From fd524e9b5eb32d6797924ae590aadd02d4c2ca12 Mon Sep 17 00:00:00 2001 From: wangli28 Date: Tue, 3 Mar 2020 13:10:07 +0000 Subject: [PATCH 05/21] Modify to include --- source/datetime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/datetime.cpp b/source/datetime.cpp index 76e31c1a..c3ae852a 100644 --- a/source/datetime.cpp +++ b/source/datetime.cpp @@ -15,7 +15,7 @@ #include #endif #include -#include +#include namespace { std::tm localtime() From 8d5e5a0962c0b88b44cc9786279141263b02b6d4 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 12 Mar 2020 23:01:51 +0800 Subject: [PATCH 06/21] add support of RLE8 compressed BMP format(#515) --- source/paint/detail/image_bmp.hpp | 85 +++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 5 deletions(-) diff --git a/source/paint/detail/image_bmp.hpp b/source/paint/detail/image_bmp.hpp index ac25812e..b38cec7c 100644 --- a/source/paint/detail/image_bmp.hpp +++ b/source/paint/detail/image_bmp.hpp @@ -1,7 +1,7 @@ /* * Bitmap Format Graphics Implementation * 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. * (See accompanying file LICENSE_1_0.txt or copy at @@ -106,7 +106,7 @@ namespace nana{ namespace paint if (16 <= header->biBitCount) pixbuf_.put(bits, header->biWidth, bmp_height, header->biBitCount, bytes_per_line, (header->biHeight < 0)); 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; } @@ -132,7 +132,72 @@ namespace nana{ namespace paint return false; } private: - void _m_put_with_palette(const bitmap_info_header* header, const unsigned char* pixel_indexes, unsigned line_bytes) + std::unique_ptr _m_decompress_rle8(const bitmap_info_header* header, const unsigned char* data, std::size_t data_size, std::size_t line_bytes) + { + 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{ 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); const std::size_t total_pixels = header->biWidth * static_cast(image_height); @@ -152,9 +217,19 @@ namespace nana{ namespace paint if (8 == header->biBitCount) { + //decompressed indexes + std::unique_ptr indexes; + + if (1 == header->biCompression) + { + indexes = _m_decompress_rle8(header, bits, length, line_bytes); + line_bytes = header->biWidth; + bits = indexes.get(); + } + 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; while (dst_px != line_end_dst_px) { @@ -173,7 +248,7 @@ namespace nana{ namespace paint { 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; std::size_t pos = 0; switch (header->biBitCount) From e8668dbc52d97fbc68350ec051eec4920e1fb81a Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 13 Mar 2020 10:09:55 +0800 Subject: [PATCH 07/21] fix place bug that splitter position drifts(#512) --- source/gui/place.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/gui/place.cpp b/source/gui/place.cpp index b9acf02e..fd2db6d3 100644 --- a/source/gui/place.cpp +++ b/source/gui/place.cpp @@ -1,7 +1,7 @@ /** * An Implementation of Place for Layout * 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. * (See accompanying file LICENSE or copy at @@ -1810,6 +1810,12 @@ namespace nana { grabbed_ = false; 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) { From 25fe4506fd3498756143e96955b16c2c8e633b45 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 14 Mar 2020 01:31:52 +0800 Subject: [PATCH 08/21] fix some GCC compilation issues --- source/gui/widgets/treebox.cpp | 4 ++-- source/paint/detail/image_bmp.hpp | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 0a114471..318d54dc 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -1736,11 +1736,11 @@ namespace nana //class trigger //struct 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) - :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) diff --git a/source/paint/detail/image_bmp.hpp b/source/paint/detail/image_bmp.hpp index b38cec7c..0e427285 100644 --- a/source/paint/detail/image_bmp.hpp +++ b/source/paint/detail/image_bmp.hpp @@ -14,6 +14,7 @@ #define NANA_PAINT_DETAIL_IMAGE_BMP_HPP #include +#include #include "image_pixbuf.hpp" namespace nana{ namespace paint @@ -132,7 +133,7 @@ namespace nana{ namespace paint return false; } private: - std::unique_ptr _m_decompress_rle8(const bitmap_info_header* header, const unsigned char* data, std::size_t data_size, std::size_t line_bytes) + std::unique_ptr _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]; @@ -222,7 +223,7 @@ namespace nana{ namespace paint if (1 == header->biCompression) { - indexes = _m_decompress_rle8(header, bits, length, line_bytes); + indexes = _m_decompress_rle8(header, bits, length); line_bytes = header->biWidth; bits = indexes.get(); } From 0771c1e0841792cdc99fcf6fd1c229b7aa291865 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Mon, 16 Mar 2020 13:15:05 +0800 Subject: [PATCH 09/21] fix a compilation error --- include/nana/gui/widgets/categorize.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/nana/gui/widgets/categorize.hpp b/include/nana/gui/widgets/categorize.hpp index 7cf90000..99ae3361 100644 --- a/include/nana/gui/widgets/categorize.hpp +++ b/include/nana/gui/widgets/categorize.hpp @@ -1,7 +1,7 @@ /** * A Categorize Implementation * 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. * (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. value_type& value() const { - return this->get_drawer_trigger().value(); + return nana::any_cast(this->get_drawer_trigger().value()); } private: //Overrides widget's virtual functions From 1597c62aa16dd5340302d24a3fd4a56a9f74fb33 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 24 Mar 2020 22:10:35 +0800 Subject: [PATCH 10/21] add supports of Hebrew and Arabic languages for unicode --- source/unicode_bidi.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/source/unicode_bidi.cpp b/source/unicode_bidi.cpp index f71645c5..b2f5f6df 100644 --- a/source/unicode_bidi.cpp +++ b/source/unicode_bidi.cpp @@ -1,7 +1,7 @@ /* * Unicode Bidi-Language Implementation * 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. * (See accompanying file LICENSE_1_0.txt or copy at @@ -990,7 +990,13 @@ namespace nana return unicode_character_type::katakana; 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; if ('\'' == ch || 0x00AD == ch || 0x00B7 == ch || 0x05F4 == ch || 0x2019 == ch || 0x2027 == ch) @@ -1025,7 +1031,7 @@ namespace nana return !(unicode_character_type::format == r_type) || (unicode_character_type::katakana == r_type); case unicode_character_type::aletter: 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; } From 48d54faeed36f9ebd95884731cbe59077b61be46 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 24 Mar 2020 22:14:08 +0800 Subject: [PATCH 11/21] fix textbox rendering issue wrong background color for selected text of RTL languges --- source/gui/widgets/skeletons/text_editor.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index d669c806..6cf84ac1 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -3483,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) { - 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_ }); graph.typeface(this->graph_.typeface()); graph.rectangle(true, selection_color(false, has_focused)); From b46966bf02dc96b09d5a8e35c6f8dbfb0dc93451 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 24 Mar 2020 22:20:04 +0800 Subject: [PATCH 12/21] fix rendering issue of RTL languages --- .../widgets/skeletons/text_token_stream.hpp | 486 +++++++++++++++--- 1 file changed, 414 insertions(+), 72 deletions(-) diff --git a/include/nana/gui/widgets/skeletons/text_token_stream.hpp b/include/nana/gui/widgets/skeletons/text_token_stream.hpp index 36560067..805840c7 100644 --- a/include/nana/gui/widgets/skeletons/text_token_stream.hpp +++ b/include/nana/gui/widgets/skeletons/text_token_stream.hpp @@ -1,7 +1,7 @@ /* * Text Token Stream * 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. * (See accompanying file LICENSE_1_0.txt or copy at @@ -44,11 +44,22 @@ namespace nana{ namespace widgets{ namespace skeletons class tokenizer { public: - tokenizer(const std::wstring& s, bool format_enabled) - : iptr_(s.data()), - endptr_(s.data() + s.size()), - format_enabled_(format_enabled) + tokenizer(const std::wstring& s, bool format_enabled) : +#if 0 //deprecated + iptr_(s.data()), + endptr_(s.data() + s.size()), +#endif + format_enabled_(format_enabled) { + entities_ = unicode_bidi{}.reorder(s.c_str(), s.size()); + for (auto & e : entities_) + { + ptr_ = e.begin; + if (e.begin < e.end) + break; + + ++idx_; + } } void push(token tk) @@ -59,18 +70,23 @@ namespace nana{ namespace widgets{ namespace skeletons //Read the token. token read() { - if(revert_token_ != token::eof) + if (revert_token_ != token::eof) { token tk = revert_token_; revert_token_ = token::eof; return tk; } - if(iptr_ == endptr_) +#if 0 //deprecated + if (iptr_ == endptr_) return token::eof; +#else + if (_m_eof()) + return token::eof; +#endif //Check whether it is a format token. - if(format_enabled_ && format_state_) + if (format_enabled_ && format_state_) return _m_format_token(); return _m_token(); @@ -106,15 +122,19 @@ namespace nana{ namespace widgets{ namespace skeletons //Read the data token token _m_token() { +#if 0 //deprecated wchar_t ch = *iptr_; - - if(ch > 0xFF) +#else + auto ch = _m_get(); +#endif + if (ch > 0xFF) { //This is the Unicode. idstr_.clear(); idstr_.append(1, ch); +#if 0 //deprecated if (_m_unicode_word_breakable(iptr_)) { ++iptr_; @@ -122,53 +142,89 @@ namespace nana{ namespace widgets{ namespace skeletons } 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); ch = *++iptr_; } +#else + if (_m_unicode_word_breakable(ptr_)) + { + _m_read(); + return token::data; + } + + _m_read(); + ch = _m_get(); + while ((!_m_eof()) && (ch > 0xFF) && (false == _m_unicode_word_breakable(ptr_))) + { + idstr_.append(1, ch); + _m_read(); + ch = _m_get(); + } + + //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); + _m_read(); +#endif return token::data; } - if('\n' == ch) + if ('\n' == ch) { +#if 0 //deprecated ++iptr_; +#else + _m_read(); +#endif return token::endl; } - if(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) + if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) { +#if 0 //deprecated auto idstr = iptr_; do { ch = *(++iptr_); - } - while(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')); + } while (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')); idstr_.assign(idstr, iptr_); +#else + auto idstr = ptr_; + do + { + _m_read(); + ch = _m_get(); + } while (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')); + idstr_.assign(idstr, ptr_); +#endif return token::data; } - if('0' <= ch && ch <= '9') + if ('0' <= ch && ch <= '9') { _m_read_number(); return token::data; } - if(('<' == ch) && format_enabled_) + if (('<' == ch) && format_enabled_) { - //pos keeps the current position, and it used for restring +#if 0 //deprecated + //pos keeps the current position, and it used for restoring //iptr_ when the search is failed. + auto pos = ++iptr_; _m_eat_whitespace(); - if(*iptr_ == '/') + if (*iptr_ == '/') { ++iptr_; _m_eat_whitespace(); - if(*iptr_ == '>') + if (*iptr_ == '>') { ++iptr_; return token::format_end; @@ -177,6 +233,29 @@ namespace nana{ namespace widgets{ namespace skeletons //Restore the iptr_; iptr_ = pos; +#else + //pos keeps the current position, and it used for restoring + //iptr_ when the search is failed. + _m_read(); + auto idx = idx_; + auto ptr = ptr_; + + _m_eat_whitespace(); + if (_m_get() == '/') + { + _m_read(); + _m_eat_whitespace(); + if (_m_get() == '>') + { + _m_read(); + return token::format_end; + } + } + + //Restore the iptr_; + idx_ = idx; + ptr_ = ptr; +#endif format_state_ = true; return token::tag_begin; @@ -184,9 +263,10 @@ namespace nana{ namespace widgets{ namespace skeletons //Escape - if(this->format_enabled_ && (ch == '\\')) +#if 0 //deprecated + if (this->format_enabled_ && (ch == '\\')) { - if(iptr_ + 1 < endptr_) + if (iptr_ + 1 < endptr_) { ch = *(iptr_ + 1); @@ -209,6 +289,32 @@ namespace nana{ namespace widgets{ namespace skeletons } else ++iptr_; +#else + if (this->format_enabled_ && (ch == '\\')) + { + if (!_m_eof(1)) + { + _m_read(); + ch = _m_get(); + if ('<' == ch || '>' == ch) //two characters need to be escaped. + { + _m_read(); + } + else + { + //ignore escape + ch = '\\'; + } + } + else + { + _m_set_eof(); + return token::eof; + } + } + else + _m_read(); +#endif idstr_.clear(); idstr_.append(1, ch); @@ -220,9 +326,12 @@ namespace nana{ namespace widgets{ namespace skeletons { _m_eat_whitespace(); +#if 0 //deprecated auto ch = *iptr_++; - - switch(ch) +#else + auto ch = _m_read(); +#endif + switch (ch) { case ',': return token::comma; case '/': return token::backslash; @@ -232,40 +341,48 @@ namespace nana{ namespace widgets{ namespace skeletons return token::tag_end; case '"': //Here is a string and all the meta characters will be ignored except " +#if 0 //deprecated + { + auto str = iptr_; + + while ((iptr_ != endptr_) && (*iptr_ != '"')) + ++iptr_; + + idstr_.assign(str, iptr_++); + } +#else + while (!(_m_eof() || ('"' == _m_get()))) { - auto str = iptr_; - - while((iptr_ != endptr_) && (*iptr_ != '"')) - ++iptr_; - - idstr_.assign(str, iptr_++); + idstr_ += _m_read(); } +#endif return token::string; case '(': _m_eat_whitespace(); - if((iptr_ < endptr_) && _m_is_idstr_element(*iptr_)) +#if 0 //deprecated + if ((iptr_ < endptr_) && _m_is_idstr_element(*iptr_)) { auto pbegin = iptr_; - while((iptr_ < endptr_) && _m_is_idstr_element(*iptr_)) + while ((iptr_ < endptr_) && _m_is_idstr_element(*iptr_)) ++iptr_; binary_.first.assign(pbegin, iptr_); _m_eat_whitespace(); - if((iptr_ < endptr_) && (',' == *iptr_)) + if ((iptr_ < endptr_) && (',' == *iptr_)) { ++iptr_; _m_eat_whitespace(); - if((iptr_ < endptr_) && _m_is_idstr_element(*iptr_)) + if ((iptr_ < endptr_) && _m_is_idstr_element(*iptr_)) { pbegin = iptr_; - while((iptr_ < endptr_) && _m_is_idstr_element(*iptr_)) + while ((iptr_ < endptr_) && _m_is_idstr_element(*iptr_)) ++iptr_; binary_.second.assign(pbegin, iptr_); _m_eat_whitespace(); - if((iptr_ < endptr_) && (')' == *iptr_)) + if ((iptr_ < endptr_) && (')' == *iptr_)) { ++iptr_; return token::binary; @@ -273,64 +390,91 @@ namespace nana{ namespace widgets{ namespace skeletons } } } +#else + if ((!_m_eof()) && _m_is_idstr_element(_m_get())) + { + while ((!_m_eof()) && _m_is_idstr_element(_m_get())) + binary_.first += _m_read(); + + _m_eat_whitespace(); + if ((!_m_eof()) && (',' == _m_get())) + { + _m_read(); + _m_eat_whitespace(); + if ((!_m_eof()) && _m_is_idstr_element(_m_get())) + { + while ((!_m_eof()) && _m_is_idstr_element(_m_get())) + binary_.second += _m_read(); + + _m_eat_whitespace(); + if ((!_m_eof()) && (')' == _m_get())) + { + _m_read(); + return token::binary; + } + } + } + } +#endif return token::eof; } - - if(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || '_' == ch) + + if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || '_' == ch) { - --iptr_; + _m_move_back(); + //Here is a identifier _m_read_idstr(); - if(L"font" == idstr_) + if (L"font" == idstr_) return token::font; - else if(L"bold" == idstr_) + else if (L"bold" == idstr_) return token::bold; - else if(L"size" == idstr_) + else if (L"size" == idstr_) return token::size; - else if(L"baseline" == idstr_) + else if (L"baseline" == idstr_) return token::baseline; - else if(L"top" == idstr_) + else if (L"top" == idstr_) return token::top; - else if(L"center" == idstr_) + else if (L"center" == idstr_) return token::center; - else if(L"bottom" == idstr_) + else if (L"bottom" == idstr_) return token::bottom; - else if(L"color" == idstr_) + else if (L"color" == idstr_) return token::color; - else if(L"image" == idstr_) + else if (L"image" == idstr_) return token::image; - else if(L"true" == idstr_) + else if (L"true" == idstr_) return token::_true; - else if(L"url" == idstr_) + else if (L"url" == idstr_) return token::url; - else if(L"target" == idstr_) + else if (L"target" == idstr_) return token::target; - else if(L"false" == idstr_) + else if (L"false" == idstr_) return token::_false; - else if(L"red" == idstr_) + else if (L"red" == idstr_) return token::red; - else if(L"green" == idstr_) + else if (L"green" == idstr_) return token::green; - else if(L"blue" == idstr_) + else if (L"blue" == idstr_) return token::blue; - else if(L"white" == idstr_) + else if (L"white" == idstr_) return token::white; - else if(L"black" == idstr_) + else if (L"black" == idstr_) return token::black; - else if(L"min_limited" == idstr_) + else if (L"min_limited" == idstr_) return token::min_limited; - else if(L"max_limited" == idstr_) + else if (L"max_limited" == idstr_) return token::max_limited; return token::string; } - if('0' <= ch && ch <= '9') + if ('0' <= ch && ch <= '9') { - --iptr_; + _m_move_back(); _m_read_number(); return token::number; } @@ -346,16 +490,28 @@ namespace nana{ namespace widgets{ namespace skeletons //Read the identifier. void _m_read_idstr() { +#if 0 //deprecated auto idstr = iptr_; wchar_t ch; do { 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_); +#else + auto idstr = ptr_; + + wchar_t ch; + do + { + _m_read(); + ch = _m_get(); + } while (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ('_' == ch) || ('0' <= ch && ch <= '9')); + + idstr_.assign(idstr, ptr_); +#endif } //Read the number @@ -363,23 +519,24 @@ namespace nana{ namespace widgets{ namespace skeletons { idstr_.clear(); +#if 0 //deprecated wchar_t ch = *iptr_; idstr_ += ch; //First check the number whether will be a hex number. - if('0' == ch) + if ('0' == ch) { ch = *++iptr_; - if((!('0' <= ch && ch <= '9')) && (ch != 'x' && ch != 'X')) + if ((!('0' <= ch && ch <= '9')) && (ch != 'x' && ch != 'X')) return; - if(ch == 'x' || ch == 'X') + if (ch == 'x' || ch == 'X') { //Here is a hex number idstr_ += 'x'; 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; ch = *++iptr_; @@ -392,18 +549,60 @@ namespace nana{ namespace widgets{ namespace skeletons } ch = *++iptr_; - while('0' <= ch && ch <= '9') + while ('0' <= ch && ch <= '9') { idstr_ += ch; ch = *++iptr_; } +#else + auto ch = _m_get(); + + idstr_ += ch; + + //First check the number whether will be a hex number. + if ('0' == ch) + { + _m_read(); + ch = _m_get(); + if ((!('0' <= ch && ch <= '9')) && (ch != 'x' && ch != 'X')) + return; + + if (ch == 'x' || ch == 'X') + { + //Here is a hex number + idstr_ += 'x'; + _m_read(); + ch = _m_get(); + while (('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')) + { + idstr_ += ch; + _m_read(); + ch = _m_get(); + } + return; + } + + //Here is not a hex number + idstr_ += ch; + } + + _m_read(); + ch = _m_get(); + while ('0' <= ch && ch <= '9') + { + idstr_ += ch; + _m_read(); + ch = _m_get(); + } +#endif } void _m_eat_whitespace() { - while(true) + while (true) { - switch(*iptr_) +#if 0 //deprecated + switch (*iptr_) { case ' ': case '\t': @@ -412,11 +611,126 @@ namespace nana{ namespace widgets{ namespace skeletons default: return; } +#else + switch (_m_get()) + { + case ' ': + case '\t': + _m_read(); + break; + default: + return; + } +#endif } } private: + wchar_t _m_get() const noexcept + { + if (idx_ < entities_.size()) + return *ptr_; + return 0; + } + + void _m_set_eof() + { + idx_ = entities_.size(); + if (0 == idx_) + ptr_ = nullptr; + else + ptr_ = entities_.back().end; + } + + bool _m_eof(std::size_t off) + { + if (0 == off) + return _m_eof(); + + bool eof = false; + auto idx = idx_; + auto ptr = ptr_; + while (off) + { + if (_m_eof()) + { + eof = true; + break; + } + + _m_read(); + } + + idx_ = idx; + ptr_ = ptr; + return eof; + } + + bool _m_eof() noexcept + { + if (idx_ == entities_.size()) + return true; + + if (ptr_ == entities_[idx_].end) + { + auto idx = idx_; + + while (++idx < entities_.size()) + { + if (entities_[idx].begin != entities_[idx].end) + return false; + } + return true; + } + + return false; + } + + wchar_t _m_read() noexcept + { + if (idx_ < entities_.size()) + { + if (ptr_ < entities_[idx_].end) + { + if (ptr_ + 1 < entities_[idx_].end) + return *(ptr_++); + } + + auto ch = *ptr_; + + while ((++idx_) < entities_.size()) + { + if (entities_[idx_].begin != entities_[idx_].end) + { + ptr_ = entities_[idx_].begin; + return ch; + } + } + } + + return 0; + } + + void _m_move_back() noexcept + { + if ((idx_ == entities_.size()) || (entities_[idx_].begin == ptr_)) + { + if (0 == idx_) + return; + --idx_; + ptr_ = entities_[idx_].end; + } + + --ptr_; + } + private: + std::vector entities_; + std::size_t idx_{ 0 }; + const wchar_t* ptr_{ nullptr }; + +#if 0 //deprecated const wchar_t * iptr_; const wchar_t * endptr_; +#endif const bool format_enabled_; bool format_state_{false}; @@ -641,6 +955,8 @@ namespace nana{ namespace widgets{ namespace skeletons while(true) { token tk = tknizer.read(); + if (token::eof == tk) + break; switch(tk) { @@ -665,12 +981,38 @@ namespace nana{ namespace widgets{ namespace skeletons if(fstack.size() > 1) fstack.pop(); break; - case token::eof: - return; default: throw std::runtime_error("invalid token"); } } + + if (!format_enabled) + return; + + //Reorder the sequence of line blocks for RTL languages. + for (auto & ln : lines_) + { + std::wstring str; + std::vector position; + for (auto & b : ln) + { + position.push_back(str.size()); + str += b.data_ptr->text(); + } + + std::remove_reference::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); + ln.push_back(dump[i - position.cbegin()]); + } + + } } iterator begin() From c9b9451443aaa394ac326ac6649faceaa4ef7873 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 26 Mar 2020 01:41:32 +0800 Subject: [PATCH 13/21] add partial support of Arabic reshaping on Linux --- .../widgets/skeletons/text_token_stream.hpp | 43 +-- include/nana/unicode_bidi.hpp | 2 +- source/detail/platform_abstraction.cpp | 44 ++- source/detail/text_reshaping.hpp | 297 ++++++++++++++++++ source/unicode_bidi.cpp | 5 + 5 files changed, 363 insertions(+), 28 deletions(-) create mode 100644 source/detail/text_reshaping.hpp diff --git a/include/nana/gui/widgets/skeletons/text_token_stream.hpp b/include/nana/gui/widgets/skeletons/text_token_stream.hpp index 805840c7..e60b4c97 100644 --- a/include/nana/gui/widgets/skeletons/text_token_stream.hpp +++ b/include/nana/gui/widgets/skeletons/text_token_stream.hpp @@ -41,11 +41,12 @@ namespace nana{ namespace widgets{ namespace skeletons eof }; +#define REORDER_TOKENSx class tokenizer { public: tokenizer(const std::wstring& s, bool format_enabled) : -#if 0 //deprecated +#ifndef REORDER_TOKENS //deprecated iptr_(s.data()), endptr_(s.data() + s.size()), #endif @@ -77,7 +78,7 @@ namespace nana{ namespace widgets{ namespace skeletons return tk; } -#if 0 //deprecated +#ifndef REORDER_TOKENS //deprecated if (iptr_ == endptr_) return token::eof; #else @@ -122,7 +123,7 @@ namespace nana{ namespace widgets{ namespace skeletons //Read the data token token _m_token() { -#if 0 //deprecated +#ifndef REORDER_TOKENS //deprecated wchar_t ch = *iptr_; #else auto ch = _m_get(); @@ -134,7 +135,7 @@ namespace nana{ namespace widgets{ namespace skeletons idstr_.clear(); idstr_.append(1, ch); -#if 0 //deprecated +#ifndef REORDER_TOKENS //deprecated if (_m_unicode_word_breakable(iptr_)) { ++iptr_; @@ -148,6 +149,11 @@ namespace nana{ namespace widgets{ namespace skeletons 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_; #else if (_m_unicode_word_breakable(ptr_)) { @@ -175,7 +181,7 @@ namespace nana{ namespace widgets{ namespace skeletons if ('\n' == ch) { -#if 0 //deprecated +#ifndef REORDER_TOKENS //deprecated ++iptr_; #else _m_read(); @@ -185,7 +191,7 @@ namespace nana{ namespace widgets{ namespace skeletons if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) { -#if 0 //deprecated +#ifndef REORDER_TOKENS //deprecated auto idstr = iptr_; do { @@ -214,7 +220,7 @@ namespace nana{ namespace widgets{ namespace skeletons if (('<' == ch) && format_enabled_) { -#if 0 //deprecated +#ifndef REORDER_TOKENS //deprecated //pos keeps the current position, and it used for restoring //iptr_ when the search is failed. @@ -263,7 +269,7 @@ namespace nana{ namespace widgets{ namespace skeletons //Escape -#if 0 //deprecated +#ifndef REORDER_TOKENS //deprecated if (this->format_enabled_ && (ch == '\\')) { if (iptr_ + 1 < endptr_) @@ -326,7 +332,7 @@ namespace nana{ namespace widgets{ namespace skeletons { _m_eat_whitespace(); -#if 0 //deprecated +#ifndef REORDER_TOKENS //deprecated auto ch = *iptr_++; #else auto ch = _m_read(); @@ -341,7 +347,7 @@ namespace nana{ namespace widgets{ namespace skeletons return token::tag_end; case '"': //Here is a string and all the meta characters will be ignored except " -#if 0 //deprecated +#ifndef REORDER_TOKENS //deprecated { auto str = iptr_; @@ -359,7 +365,7 @@ namespace nana{ namespace widgets{ namespace skeletons return token::string; case '(': _m_eat_whitespace(); -#if 0 //deprecated +#ifndef REORDER_TOKENS //deprecated if ((iptr_ < endptr_) && _m_is_idstr_element(*iptr_)) { auto pbegin = iptr_; @@ -423,7 +429,11 @@ namespace nana{ namespace widgets{ namespace skeletons if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || '_' == ch) { +#ifndef REORDER_TOKENS //deprecated + --iptr_; +#else _m_move_back(); +#endif //Here is a identifier _m_read_idstr(); @@ -490,7 +500,7 @@ namespace nana{ namespace widgets{ namespace skeletons //Read the identifier. void _m_read_idstr() { -#if 0 //deprecated +#ifndef REORDER_TOKENS //deprecated auto idstr = iptr_; wchar_t ch; @@ -519,7 +529,7 @@ namespace nana{ namespace widgets{ namespace skeletons { idstr_.clear(); -#if 0 //deprecated +#ifndef REORDER_TOKENS //deprecated wchar_t ch = *iptr_; idstr_ += ch; @@ -601,7 +611,7 @@ namespace nana{ namespace widgets{ namespace skeletons { while (true) { -#if 0 //deprecated +#ifndef REORDER_TOKENS //deprecated switch (*iptr_) { case ' ': @@ -727,7 +737,7 @@ namespace nana{ namespace widgets{ namespace skeletons std::size_t idx_{ 0 }; const wchar_t* ptr_{ nullptr }; -#if 0 //deprecated +#ifndef REORDER_TOKENS //deprecated const wchar_t * iptr_; const wchar_t * endptr_; #endif @@ -986,9 +996,6 @@ namespace nana{ namespace widgets{ namespace skeletons } } - if (!format_enabled) - return; - //Reorder the sequence of line blocks for RTL languages. for (auto & ln : lines_) { diff --git a/include/nana/unicode_bidi.hpp b/include/nana/unicode_bidi.hpp index 5efd7003..2d745167 100644 --- a/include/nana/unicode_bidi.hpp +++ b/include/nana/unicode_bidi.hpp @@ -50,6 +50,7 @@ namespace nana }; std::vector reorder(const char_type*, std::size_t len); + static bool is_text_right(const entity&); private: static unsigned _m_paragraph_level(const char_type * begin, const char_type * end); @@ -72,7 +73,6 @@ namespace nana std::vector unicode_reorder(const wchar_t* text, std::size_t length); bool unicode_wordbreak(wchar_t left, wchar_t right); - } #include diff --git a/source/detail/platform_abstraction.cpp b/source/detail/platform_abstraction.cpp index ea6032b2..d540ea1a 100644 --- a/source/detail/platform_abstraction.cpp +++ b/source/detail/platform_abstraction.cpp @@ -146,6 +146,8 @@ IsWindows8OrGreater() # include "posix/platform_spec.hpp" # include # if defined(NANA_USE_XFT) +# include +# include "text_reshaping.hpp" # include # include # include @@ -211,16 +213,40 @@ namespace nana int const init_x = x; std::unique_ptr 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()); - x += _m_draw(xftdraw, xftcolor, preferred.first, x, y, str, preferred.second, glyph_indexes.get()); + auto size = static_cast(e.end - e.begin); + auto p = e.begin; - if(len == preferred.second) - break; + if(unicode_bidi::is_text_right(e)) + { + std::wstring reverse_str; + for(auto i = static_cast(e.end - e.begin) - 1; i > -1; --i) + reverse_str += e.begin[i]; - len -= preferred.second; - str += preferred.second; + //Process reshaping. + rtl = nana::reshaping::arabic::reshape(reverse_str); + p = rtl.c_str(); + 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; @@ -298,14 +324,14 @@ namespace nana if(ptab == p) { ++p; - //x += static_cast(tab_pixels_); continue; } auto const size = ptab - p; + ::XftDrawGlyphs(xftdraw, xftcolor, xft, x, y, glyph_indexes + off, size); ::XftGlyphExtents(disp_, xft, glyph_indexes + off, size, &ext); - + x += ext.xOff; if(ptab == end) diff --git a/source/detail/text_reshaping.hpp b/source/detail/text_reshaping.hpp new file mode 100644 index 00000000..31bfac54 --- /dev/null +++ b/source/detail/text_reshaping.hpp @@ -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(letter - ranges[i][0]) + base; + + base += static_cast(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 forms; + + std::map positions_harakat; + + for(auto letter: text) + { + if(harakat(letter)) + { + if(!delete_harakat) + { + int position = static_cast(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 +} \ No newline at end of file diff --git a/source/unicode_bidi.cpp b/source/unicode_bidi.cpp index b2f5f6df..5957981e 100644 --- a/source/unicode_bidi.cpp +++ b/source/unicode_bidi.cpp @@ -606,6 +606,11 @@ namespace nana 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) { for(const char_type* i = begin; i != end; ++i) From 419d615505a9dff81c8ac392adc2de5c890a53ce Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 27 Mar 2020 07:28:48 +0800 Subject: [PATCH 14/21] listbox::hovered now returns absolute position fix related issue #528 --- include/nana/gui/widgets/listbox.hpp | 12 +++++++++++- source/gui/widgets/listbox.cpp | 16 +++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index cd2da24b..4856a7f3 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -1,7 +1,7 @@ /** * A List Box Implementation * 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. * (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. 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 /** + * 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. * @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. */ diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index d9561985..2464b3d7 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -1,7 +1,7 @@ /* * A List Box Implementation * 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. * (See accompanying file LICENSE_1_0.txt or copy at @@ -5736,6 +5736,14 @@ namespace nana 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 { using parts = drawerbase::listbox::essence::parts; @@ -5755,9 +5763,11 @@ namespace nana if (0 < pos.cat) --pos.cat; pos.item = this->size_item(pos.cat); - } - return pos; + return pos; + } + + return index_cast(pos, true); } else if (return_end) return index_pair{ this->size_categ() - 1, this->size_item(this->size_categ() - 1) }; From 78a6ad634454fcfafb733cab2f0c9f3d367ed407 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 27 Mar 2020 07:35:58 +0800 Subject: [PATCH 15/21] fix issue that output of glyph pixel is wrong --- source/detail/platform_abstraction.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/detail/platform_abstraction.cpp b/source/detail/platform_abstraction.cpp index d540ea1a..ce0ad993 100644 --- a/source/detail/platform_abstraction.cpp +++ b/source/detail/platform_abstraction.cpp @@ -385,10 +385,10 @@ namespace nana if('\t' != *p) { ::XftGlyphExtents(disp_, xft, glyph_indexes, 1, &extent); - *pxbuf = extent.xOff; + *pxbuf++ = extent.xOff; } else - *pxbuf = 0;//tab_pixels_; + *pxbuf++ = 0;//tab_pixels_; ++glyph_indexes; } From bdc64b2c8a899c19f6286c6bc6ce2f59dc2004b8 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 1 Apr 2020 05:11:55 +0800 Subject: [PATCH 16/21] fix arabic reshaping issues --- source/detail/platform_abstraction.cpp | 89 ++++++++++++++++++-------- 1 file changed, 63 insertions(+), 26 deletions(-) diff --git a/source/detail/platform_abstraction.cpp b/source/detail/platform_abstraction.cpp index ce0ad993..43941c15 100644 --- a/source/detail/platform_abstraction.cpp +++ b/source/detail/platform_abstraction.cpp @@ -223,12 +223,9 @@ namespace nana if(unicode_bidi::is_text_right(e)) { - std::wstring reverse_str; - for(auto i = static_cast(e.end - e.begin) - 1; i > -1; --i) - reverse_str += e.begin[i]; + auto restr = nana::reshaping::arabic::reshape(std::wstring{e.begin, e.end}); + rtl.assign(restr.crbegin(), restr.crend()); - //Process reshaping. - rtl = nana::reshaping::arabic::reshape(reverse_str); p = rtl.c_str(); size = rtl.size(); } @@ -262,22 +259,23 @@ namespace nana std::unique_ptr pxbuf{new unsigned[len]}; auto pbuf = pxbuf.get(); - auto pstr = str; - auto size = len; - while(true) - { - auto preferred = _m_scan_fonts(xft, pstr, size, glyph_indexes.get()); + //Don't reverse the string + _m_reorder_reshaping(std::wstring_view{str, len}, 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) - break; + if(size == preferred.second) + break; - size -= preferred.second; - pstr += preferred.second; - pbuf += preferred.second; - } + size -= preferred.second; + p += preferred.second; + pstr += preferred.second; + } + }); return pxbuf; } @@ -291,21 +289,60 @@ namespace nana std::unique_ptr glyph_indexes(new FT_UInt[len]); - while(len > 0) - { - auto preferred = _m_scan_fonts(xft, str, len, glyph_indexes.get()); + //Don't reverse the string + _m_reorder_reshaping(std::wstring_view{str, len}, 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(extent.height)) - extent.height = preferred.first->ascent + preferred.first->descent; + if(preferred.first->ascent + preferred.first->descent > static_cast(extent.height)) + 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; } private: + /// @param reverse Indicates whether to reverse the string, it only reverse the RTL language string. + template + void _m_reorder_reshaping(std::wstring_view str, bool reverse, Function fn) + { + //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(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 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) { From c5ce97734fa0553c127877d6d7fd44a5521226bb Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 17 Apr 2020 00:23:35 +0800 Subject: [PATCH 17/21] fix crash that was introduced by patch of Arabic language support --- .../widgets/skeletons/text_token_stream.hpp | 331 ++---------------- 1 file changed, 21 insertions(+), 310 deletions(-) diff --git a/include/nana/gui/widgets/skeletons/text_token_stream.hpp b/include/nana/gui/widgets/skeletons/text_token_stream.hpp index e60b4c97..7494dd4f 100644 --- a/include/nana/gui/widgets/skeletons/text_token_stream.hpp +++ b/include/nana/gui/widgets/skeletons/text_token_stream.hpp @@ -41,15 +41,13 @@ namespace nana{ namespace widgets{ namespace skeletons eof }; -#define REORDER_TOKENSx + class tokenizer { public: tokenizer(const std::wstring& s, bool format_enabled) : -#ifndef REORDER_TOKENS //deprecated iptr_(s.data()), endptr_(s.data() + s.size()), -#endif format_enabled_(format_enabled) { entities_ = unicode_bidi{}.reorder(s.c_str(), s.size()); @@ -78,13 +76,8 @@ namespace nana{ namespace widgets{ namespace skeletons return tk; } -#ifndef REORDER_TOKENS //deprecated if (iptr_ == endptr_) return token::eof; -#else - if (_m_eof()) - return token::eof; -#endif //Check whether it is a format token. if (format_enabled_ && format_state_) @@ -123,11 +116,7 @@ namespace nana{ namespace widgets{ namespace skeletons //Read the data token token _m_token() { -#ifndef REORDER_TOKENS //deprecated wchar_t ch = *iptr_; -#else - auto ch = _m_get(); -#endif if (ch > 0xFF) { //This is the Unicode. @@ -135,7 +124,6 @@ namespace nana{ namespace widgets{ namespace skeletons idstr_.clear(); idstr_.append(1, ch); -#ifndef REORDER_TOKENS //deprecated if (_m_unicode_word_breakable(iptr_)) { ++iptr_; @@ -154,44 +142,18 @@ namespace nana{ namespace widgets{ namespace skeletons //is not the breakable character. So it belongs to the data. idstr_.append(1, ch); ++iptr_; -#else - if (_m_unicode_word_breakable(ptr_)) - { - _m_read(); - return token::data; - } - - _m_read(); - ch = _m_get(); - while ((!_m_eof()) && (ch > 0xFF) && (false == _m_unicode_word_breakable(ptr_))) - { - idstr_.append(1, ch); - _m_read(); - ch = _m_get(); - } - - //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); - _m_read(); -#endif return token::data; } if ('\n' == ch) { -#ifndef REORDER_TOKENS //deprecated ++iptr_; -#else - _m_read(); -#endif return token::endl; } if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) { -#ifndef REORDER_TOKENS //deprecated auto idstr = iptr_; do { @@ -199,16 +161,7 @@ namespace nana{ namespace widgets{ namespace skeletons } while (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')); idstr_.assign(idstr, iptr_); -#else - auto idstr = ptr_; - do - { - _m_read(); - ch = _m_get(); - } while (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')); - idstr_.assign(idstr, ptr_); -#endif return token::data; } @@ -220,7 +173,6 @@ namespace nana{ namespace widgets{ namespace skeletons if (('<' == ch) && format_enabled_) { -#ifndef REORDER_TOKENS //deprecated //pos keeps the current position, and it used for restoring //iptr_ when the search is failed. @@ -239,37 +191,12 @@ namespace nana{ namespace widgets{ namespace skeletons //Restore the iptr_; iptr_ = pos; -#else - //pos keeps the current position, and it used for restoring - //iptr_ when the search is failed. - _m_read(); - auto idx = idx_; - auto ptr = ptr_; - - _m_eat_whitespace(); - if (_m_get() == '/') - { - _m_read(); - _m_eat_whitespace(); - if (_m_get() == '>') - { - _m_read(); - return token::format_end; - } - } - - //Restore the iptr_; - idx_ = idx; - ptr_ = ptr; -#endif format_state_ = true; return token::tag_begin; } - //Escape -#ifndef REORDER_TOKENS //deprecated if (this->format_enabled_ && (ch == '\\')) { if (iptr_ + 1 < endptr_) @@ -295,32 +222,6 @@ namespace nana{ namespace widgets{ namespace skeletons } else ++iptr_; -#else - if (this->format_enabled_ && (ch == '\\')) - { - if (!_m_eof(1)) - { - _m_read(); - ch = _m_get(); - if ('<' == ch || '>' == ch) //two characters need to be escaped. - { - _m_read(); - } - else - { - //ignore escape - ch = '\\'; - } - } - else - { - _m_set_eof(); - return token::eof; - } - } - else - _m_read(); -#endif idstr_.clear(); idstr_.append(1, ch); @@ -332,11 +233,7 @@ namespace nana{ namespace widgets{ namespace skeletons { _m_eat_whitespace(); -#ifndef REORDER_TOKENS //deprecated auto ch = *iptr_++; -#else - auto ch = _m_read(); -#endif switch (ch) { case ',': return token::comma; @@ -347,7 +244,6 @@ namespace nana{ namespace widgets{ namespace skeletons return token::tag_end; case '"': //Here is a string and all the meta characters will be ignored except " -#ifndef REORDER_TOKENS //deprecated { auto str = iptr_; @@ -356,16 +252,9 @@ namespace nana{ namespace widgets{ namespace skeletons idstr_.assign(str, iptr_++); } -#else - while (!(_m_eof() || ('"' == _m_get()))) - { - idstr_ += _m_read(); - } -#endif return token::string; case '(': _m_eat_whitespace(); -#ifndef REORDER_TOKENS //deprecated if ((iptr_ < endptr_) && _m_is_idstr_element(*iptr_)) { auto pbegin = iptr_; @@ -396,32 +285,7 @@ namespace nana{ namespace widgets{ namespace skeletons } } } -#else - if ((!_m_eof()) && _m_is_idstr_element(_m_get())) - { - while ((!_m_eof()) && _m_is_idstr_element(_m_get())) - binary_.first += _m_read(); - _m_eat_whitespace(); - if ((!_m_eof()) && (',' == _m_get())) - { - _m_read(); - _m_eat_whitespace(); - if ((!_m_eof()) && _m_is_idstr_element(_m_get())) - { - while ((!_m_eof()) && _m_is_idstr_element(_m_get())) - binary_.second += _m_read(); - - _m_eat_whitespace(); - if ((!_m_eof()) && (')' == _m_get())) - { - _m_read(); - return token::binary; - } - } - } - } -#endif return token::eof; } @@ -429,11 +293,7 @@ namespace nana{ namespace widgets{ namespace skeletons if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || '_' == ch) { -#ifndef REORDER_TOKENS //deprecated --iptr_; -#else - _m_move_back(); -#endif //Here is a identifier _m_read_idstr(); @@ -484,7 +344,7 @@ namespace nana{ namespace widgets{ namespace skeletons if ('0' <= ch && ch <= '9') { - _m_move_back(); + --iptr_; _m_read_number(); return token::number; } @@ -500,7 +360,6 @@ namespace nana{ namespace widgets{ namespace skeletons //Read the identifier. void _m_read_idstr() { -#ifndef REORDER_TOKENS //deprecated auto idstr = iptr_; wchar_t ch; @@ -510,18 +369,6 @@ namespace nana{ namespace widgets{ namespace skeletons } while (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ('_' == ch) || ('0' <= ch && ch <= '9')); idstr_.assign(idstr, iptr_); -#else - auto idstr = ptr_; - - wchar_t ch; - do - { - _m_read(); - ch = _m_get(); - } while (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ('_' == ch) || ('0' <= ch && ch <= '9')); - - idstr_.assign(idstr, ptr_); -#endif } //Read the number @@ -529,7 +376,6 @@ namespace nana{ namespace widgets{ namespace skeletons { idstr_.clear(); -#ifndef REORDER_TOKENS //deprecated wchar_t ch = *iptr_; idstr_ += ch; @@ -564,54 +410,12 @@ namespace nana{ namespace widgets{ namespace skeletons idstr_ += ch; ch = *++iptr_; } -#else - auto ch = _m_get(); - - idstr_ += ch; - - //First check the number whether will be a hex number. - if ('0' == ch) - { - _m_read(); - ch = _m_get(); - if ((!('0' <= ch && ch <= '9')) && (ch != 'x' && ch != 'X')) - return; - - if (ch == 'x' || ch == 'X') - { - //Here is a hex number - idstr_ += 'x'; - _m_read(); - ch = _m_get(); - while (('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')) - { - idstr_ += ch; - _m_read(); - ch = _m_get(); - } - return; - } - - //Here is not a hex number - idstr_ += ch; - } - - _m_read(); - ch = _m_get(); - while ('0' <= ch && ch <= '9') - { - idstr_ += ch; - _m_read(); - ch = _m_get(); - } -#endif } void _m_eat_whitespace() { while (true) { -#ifndef REORDER_TOKENS //deprecated switch (*iptr_) { case ' ': @@ -621,126 +425,15 @@ namespace nana{ namespace widgets{ namespace skeletons default: return; } -#else - switch (_m_get()) - { - case ' ': - case '\t': - _m_read(); - break; - default: - return; - } -#endif } } - private: - wchar_t _m_get() const noexcept - { - if (idx_ < entities_.size()) - return *ptr_; - return 0; - } - - void _m_set_eof() - { - idx_ = entities_.size(); - if (0 == idx_) - ptr_ = nullptr; - else - ptr_ = entities_.back().end; - } - - bool _m_eof(std::size_t off) - { - if (0 == off) - return _m_eof(); - - bool eof = false; - auto idx = idx_; - auto ptr = ptr_; - while (off) - { - if (_m_eof()) - { - eof = true; - break; - } - - _m_read(); - } - - idx_ = idx; - ptr_ = ptr; - return eof; - } - - bool _m_eof() noexcept - { - if (idx_ == entities_.size()) - return true; - - if (ptr_ == entities_[idx_].end) - { - auto idx = idx_; - - while (++idx < entities_.size()) - { - if (entities_[idx].begin != entities_[idx].end) - return false; - } - return true; - } - - return false; - } - - wchar_t _m_read() noexcept - { - if (idx_ < entities_.size()) - { - if (ptr_ < entities_[idx_].end) - { - if (ptr_ + 1 < entities_[idx_].end) - return *(ptr_++); - } - - auto ch = *ptr_; - - while ((++idx_) < entities_.size()) - { - if (entities_[idx_].begin != entities_[idx_].end) - { - ptr_ = entities_[idx_].begin; - return ch; - } - } - } - - return 0; - } - - void _m_move_back() noexcept - { - if ((idx_ == entities_.size()) || (entities_[idx_].begin == ptr_)) - { - if (0 == idx_) - return; - --idx_; - ptr_ = entities_[idx_].end; - } - - --ptr_; - } private: std::vector entities_; std::size_t idx_{ 0 }; const wchar_t* ptr_{ nullptr }; -#ifndef REORDER_TOKENS //deprecated const wchar_t * iptr_; const wchar_t * endptr_; -#endif const bool format_enabled_; bool format_state_{false}; @@ -1000,6 +693,7 @@ namespace nana{ namespace widgets{ namespace skeletons for (auto & ln : lines_) { std::wstring str; + //Position only holds the start positions of blocks in a line. std::vector position; for (auto & b : ln) { @@ -1016,9 +710,26 @@ namespace nana{ namespace widgets{ namespace skeletons 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 < endpos) + { + ln.push_back(dump[i - position.cbegin()]); + } + else + break; + } } - } } From cffea79e0f7afbbe33f42163cd2ea2551db9c299 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 17 Apr 2020 07:23:19 +0800 Subject: [PATCH 18/21] remove deprecated code --- .../gui/widgets/skeletons/text_token_stream.hpp | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/include/nana/gui/widgets/skeletons/text_token_stream.hpp b/include/nana/gui/widgets/skeletons/text_token_stream.hpp index 7494dd4f..ffe5214c 100644 --- a/include/nana/gui/widgets/skeletons/text_token_stream.hpp +++ b/include/nana/gui/widgets/skeletons/text_token_stream.hpp @@ -50,15 +50,6 @@ namespace nana{ namespace widgets{ namespace skeletons endptr_(s.data() + s.size()), format_enabled_(format_enabled) { - entities_ = unicode_bidi{}.reorder(s.c_str(), s.size()); - for (auto & e : entities_) - { - ptr_ = e.begin; - if (e.begin < e.end) - break; - - ++idx_; - } } void push(token tk) @@ -428,10 +419,6 @@ namespace nana{ namespace widgets{ namespace skeletons } } private: - std::vector entities_; - std::size_t idx_{ 0 }; - const wchar_t* ptr_{ nullptr }; - const wchar_t * iptr_; const wchar_t * endptr_; const bool format_enabled_; @@ -722,7 +709,7 @@ namespace nana{ namespace widgets{ namespace skeletons //Check whether the next position is belone to current entity. while (++i != position.cend()) { - if (*i < endpos) + if (*i < static_cast(endpos)) { ln.push_back(dump[i - position.cbegin()]); } From 4dcbb50e1f5ba48eef6f4aa805e861aad80d2cb6 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 18 Apr 2020 02:48:13 +0800 Subject: [PATCH 19/21] fix the missing of overloaded non-const of treebox::value/value_ptr --- include/nana/gui/widgets/treebox.hpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/nana/gui/widgets/treebox.hpp b/include/nana/gui/widgets/treebox.hpp index eaae22e5..a6342404 100644 --- a/include/nana/gui/widgets/treebox.hpp +++ b/include/nana/gui/widgets/treebox.hpp @@ -300,6 +300,12 @@ namespace nana return any_cast(&_m_value()); } + template + T * value_ptr() + { + return any_cast(&_m_value()); + } + template const T& value() const { @@ -309,6 +315,15 @@ namespace nana return *p; } + template + T& value() + { + auto p = any_cast(&_m_value()); + if (nullptr == p) + throw std::runtime_error("treebox::value() Invalid type of value."); + return *p; + } + template item_proxy & value(T&& t) { From d3e3e59812bf800aae84913743a589fd295ef4bc Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 18 Apr 2020 02:59:12 +0800 Subject: [PATCH 20/21] open image file using file format signature instead of file extension --- source/paint/image.cpp | 171 +++++++++++------------------------------ 1 file changed, 44 insertions(+), 127 deletions(-) diff --git a/source/paint/image.cpp b/source/paint/image.cpp index 389ed68e..a2e096fb 100644 --- a/source/paint/image.cpp +++ b/source/paint/image.cpp @@ -1,7 +1,7 @@ /** * Paint Image Implementation * 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. * (See accompanying file LICENSE_1_0.txt or copy at @@ -108,155 +108,72 @@ namespace paint return *this; } - std::shared_ptr create_image(const fs::path & p) + // Check file type through file format signature + std::shared_ptr create_image(const char* buf, std::size_t len) { - std::shared_ptr ptr; - - auto ext = p.extension().native(); - if (ext.empty()) - return ptr; - - std::transform(ext.begin(), ext.end(), ext.begin(), [](int ch) + if (buf && len >= 8) { - if ('A' <= ch && ch <= 'Z') - 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) + if (std::strncmp("\x00\x00\x01\x00", buf, 4) == 0) { - ptr = std::make_shared(); - break; + return std::make_shared(); } - - if (ext_png == ext) + else if (std::strncmp(buf, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == 0) { #if defined(NANA_ENABLE_PNG) - ptr = std::make_shared(); -#else - return ptr; + return std::make_shared(); #endif - break; } - - if (ext_jpg == ext || ext_jpeg == ext) + else if (std::strncmp("\xFF\xD8\xFF", buf, 3) == 0) { #if defined(NANA_ENABLE_JPEG) - ptr = std::make_shared(); -#else - return ptr; + return std::make_shared(); #endif - break; } - } while (false); + else if (*reinterpret_cast("BM") == *reinterpret_cast(buf)) + return std::make_shared(); + else if (*reinterpret_cast("MZ") == *reinterpret_cast(buf)) + return std::make_shared(); + } - //Check for BMP - 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(&meta), 2); - if (*reinterpret_cast("BM") == meta) - ptr = std::make_shared(); - else if (*reinterpret_cast("MZ") == meta) - ptr = std::make_shared(); - } + return nullptr; } - 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); - image_ptr_ = create_image(path); - return (image_ptr_ ? image_ptr_->open(path) : false); - } + fs::path p(img); + image_ptr_.reset(); - bool image::open(const std::wstring& file) - { - fs::path path(file); - image_ptr_ = create_image(path); - return (image_ptr_ ? image_ptr_->open(path) : false); + 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 void* data, std::size_t bytes) { - close(); - - if (bytes > 2) - { - std::shared_ptr ptr; - - auto meta = *reinterpret_cast(data); - - if (*reinterpret_cast("BM") == meta) - ptr = std::make_shared(); - else if (*reinterpret_cast("MZ") == meta) - ptr = std::make_shared(); - else - { - if (bytes > 8 && (0x474e5089 == *reinterpret_cast(data))) - { -#if defined(NANA_ENABLE_PNG) - ptr = std::make_shared(); -#endif - } - else - { -#if defined(NANA_ENABLE_JPEG) - if ((bytes > 11) && (0xd8ff == *reinterpret_cast(data))) - { - switch(*reinterpret_cast(reinterpret_cast(data)+6)) - { - case 0x4649464A: //JFIF - case 0x66697845: //Exif - ptr = std::make_shared(); - } - } - else -#endif - if ((!ptr) && (bytes > 40)) - { - switch (*reinterpret_cast(data)) - { - case 40: - case 0x00010000: - if (!ptr && bytes > 40) - ptr = std::make_shared(); - } - } - } - } - - - if (ptr) - { - image_ptr_.swap(ptr); - return image_ptr_->open(data, bytes); - } - } - - return false; + image_ptr_ = create_image(static_cast(data), bytes); + return (image_ptr_ ? image_ptr_->open(data, bytes) : false); } From e09b57ca126e5f43da6273a437ab488873f6f217 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 18 Apr 2020 04:34:59 +0800 Subject: [PATCH 21/21] fix compiler errors using C++11 --- .../widgets/skeletons/text_token_stream.hpp | 1 + source/detail/platform_abstraction.cpp | 21 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/include/nana/gui/widgets/skeletons/text_token_stream.hpp b/include/nana/gui/widgets/skeletons/text_token_stream.hpp index ffe5214c..5c49f735 100644 --- a/include/nana/gui/widgets/skeletons/text_token_stream.hpp +++ b/include/nana/gui/widgets/skeletons/text_token_stream.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include diff --git a/source/detail/platform_abstraction.cpp b/source/detail/platform_abstraction.cpp index 43941c15..758c0983 100644 --- a/source/detail/platform_abstraction.cpp +++ b/source/detail/platform_abstraction.cpp @@ -2,6 +2,9 @@ #include #include #include "../paint/truetype.hpp" +#ifdef _nana_std_has_string_view +# include +#endif #ifdef NANA_WINDOWS @@ -260,8 +263,13 @@ namespace nana auto pbuf = pxbuf.get(); +#ifdef _nana_std_has_string_view + std::wstring_view s{str, len}; +#else + std::wstring s{str, len}; +#endif //Don't reverse the string - _m_reorder_reshaping(std::wstring_view{str, len}, false, [&,xft, str](const wchar_t* p, std::size_t size, const wchar_t* pstr) mutable{ + _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()); @@ -289,8 +297,13 @@ namespace nana std::unique_ptr glyph_indexes(new FT_UInt[len]); +#ifdef _nana_std_has_string_view + std::wstring_view s{str, len}; +#else + std::wstring s{str, len}; +#endif //Don't reverse the string - _m_reorder_reshaping(std::wstring_view{str, len}, false, [&,xft, str](const wchar_t* p, std::size_t size, const wchar_t* /*pstr*/) mutable{ + _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()); @@ -313,7 +326,11 @@ namespace nana private: /// @param reverse Indicates whether to reverse the string, it only reverse the RTL language string. template +#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;