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() # ?? diff --git a/build/cmake/select_filesystem.cmake b/build/cmake/select_filesystem.cmake index abd82cb4..d1f24816 100644 --- a/build/cmake/select_filesystem.cmake +++ b/build/cmake/select_filesystem.cmake @@ -53,7 +53,7 @@ else() if(NANA_CMAKE_STD_FILESYSTEM_FORCE) target_compile_definitions(nana PUBLIC STD_FILESYSTEM_FORCE) endif() - + include (CheckIncludeFileCXX) check_include_file_cxx (filesystem NANA_HAVE_FILESYSTEM) if (NANA_HAVE_FILESYSTEM) message (STATUS "C++ Filesystem header: ") @@ -87,7 +87,6 @@ else() if (TEST_FS_LIB) include (FindPackageMessage) - include (CheckIncludeFileCXX) include (CheckCXXSourceCompiles) # CMAKE_REQUIRED_FLAGS = string of compile command line flags # CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) diff --git a/include/nana/gui/animation.hpp b/include/nana/gui/animation.hpp index f0267a46..f3a648bc 100644 --- a/include/nana/gui/animation.hpp +++ b/include/nana/gui/animation.hpp @@ -1,7 +1,7 @@ /* * An Animation Implementation * 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 @@ -29,16 +29,15 @@ namespace nana public: /// function which builds frames. using framebuilder = std::function; - - struct impl; public: frameset(); void push_back(paint::image); ///< Inserts frames at the end. void push_back(framebuilder fb, std::size_t length); ///< Inserts a framebuilder and the number of frames that it generates. private: + struct impl; std::shared_ptr impl_; }; - /// Easy way to display an animation or create an animated GUI + /// Easy way to display an animation or create an animated GUI class animation { struct branch_t @@ -46,7 +45,7 @@ namespace nana frameset frames; std::function condition; }; - + struct impl; class performance_manager; @@ -68,12 +67,21 @@ namespace nana void pause(); + /// Renders the animation at a fixed position void output(window wd, const nana::point& pos); + /// Renders the animation at a rectangle + /** + * If the size of rectangle is not equal to the size of frame, it stretches the frame for the size of rectangle. + * @param wd Output window. + * @param r Generator of the rectangle. The generator gets called every time rendering occurs. + */ + void output(window wd, std::function r); + void fps(std::size_t n); std::size_t fps() const; private: - impl * impl_; + std::unique_ptr impl_; }; } //end namespace nana #include diff --git a/include/nana/gui/basis.hpp b/include/nana/gui/basis.hpp index e6ee771c..fdb86db6 100644 --- a/include/nana/gui/basis.hpp +++ b/include/nana/gui/basis.hpp @@ -33,7 +33,7 @@ namespace nana struct accel_key { - char key; + char key{ '\0' }; bool case_sensitive{ false }; bool alt{ false }; bool ctrl{ false }; 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 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/include/nana/gui/widgets/skeletons/text_token_stream.hpp b/include/nana/gui/widgets/skeletons/text_token_stream.hpp index 36560067..5c49f735 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 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -41,13 +42,14 @@ namespace nana{ namespace widgets{ namespace skeletons eof }; + 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) : + iptr_(s.data()), + endptr_(s.data() + s.size()), + format_enabled_(format_enabled) { } @@ -59,18 +61,18 @@ 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 (iptr_ == endptr_) return token::eof; //Check whether it is a format token. - if(format_enabled_ && format_state_) + if (format_enabled_ && format_state_) return _m_format_token(); return _m_token(); @@ -107,8 +109,7 @@ namespace nana{ namespace widgets{ namespace skeletons token _m_token() { wchar_t ch = *iptr_; - - if(ch > 0xFF) + if (ch > 0xFF) { //This is the Unicode. @@ -122,53 +123,58 @@ 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_; } + //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; } - if('\n' == ch) + if ('\n' == ch) { ++iptr_; return token::endl; } - if(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) + if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) { 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_); 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 + //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; @@ -182,11 +188,10 @@ namespace nana{ namespace widgets{ namespace skeletons return token::tag_begin; } - //Escape - if(this->format_enabled_ && (ch == '\\')) + if (this->format_enabled_ && (ch == '\\')) { - if(iptr_ + 1 < endptr_) + if (iptr_ + 1 < endptr_) { ch = *(iptr_ + 1); @@ -221,8 +226,7 @@ namespace nana{ namespace widgets{ namespace skeletons _m_eat_whitespace(); auto ch = *iptr_++; - - switch(ch) + switch (ch) { case ',': return token::comma; case '/': return token::backslash; @@ -232,40 +236,40 @@ namespace nana{ namespace widgets{ namespace skeletons return token::tag_end; case '"': //Here is a string and all the meta characters will be ignored except " - { - auto str = iptr_; + { + auto str = iptr_; - while((iptr_ != endptr_) && (*iptr_ != '"')) - ++iptr_; + while ((iptr_ != endptr_) && (*iptr_ != '"')) + ++iptr_; - idstr_.assign(str, iptr_++); - } + idstr_.assign(str, iptr_++); + } return token::string; case '(': _m_eat_whitespace(); - if((iptr_ < endptr_) && _m_is_idstr_element(*iptr_)) + 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,62 +277,64 @@ namespace nana{ namespace widgets{ namespace skeletons } } } + return token::eof; } - - if(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || '_' == ch) + + if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || '_' == ch) { --iptr_; + //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_read_number(); @@ -352,8 +358,7 @@ namespace nana{ namespace widgets{ namespace skeletons 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_); } @@ -368,18 +373,18 @@ namespace nana{ namespace widgets{ namespace skeletons 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,7 +397,7 @@ namespace nana{ namespace widgets{ namespace skeletons } ch = *++iptr_; - while('0' <= ch && ch <= '9') + while ('0' <= ch && ch <= '9') { idstr_ += ch; ch = *++iptr_; @@ -401,9 +406,9 @@ namespace nana{ namespace widgets{ namespace skeletons void _m_eat_whitespace() { - while(true) + while (true) { - switch(*iptr_) + switch (*iptr_) { case ' ': case '\t': @@ -641,6 +646,8 @@ namespace nana{ namespace widgets{ namespace skeletons while(true) { token tk = tknizer.read(); + if (token::eof == tk) + break; switch(tk) { @@ -665,12 +672,53 @@ namespace nana{ namespace widgets{ namespace skeletons if(fstack.size() > 1) fstack.pop(); break; - case token::eof: - return; default: 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 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); + + //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(endpos)) + { + ln.push_back(dump[i - position.cbegin()]); + } + else + break; + } + } + } } iterator begin() diff --git a/include/nana/gui/widgets/treebox.hpp b/include/nana/gui/widgets/treebox.hpp index eaae22e5..22dc4029 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) { @@ -500,6 +515,8 @@ namespace nana */ void use_entire_line(bool enable); + /// Return the first node of treebox + item_proxy first() const; private: std::shared_ptr _m_scroll_operation() override; 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/datetime.cpp b/source/datetime.cpp index e6e8fa5b..c3ae852a 100644 --- a/source/datetime.cpp +++ b/source/datetime.cpp @@ -15,6 +15,7 @@ #include #endif #include +#include namespace { std::tm localtime() diff --git a/source/detail/platform_abstraction.cpp b/source/detail/platform_abstraction.cpp index ea6032b2..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 @@ -146,6 +149,8 @@ IsWindows8OrGreater() # include "posix/platform_spec.hpp" # include # if defined(NANA_USE_XFT) +# include +# include "text_reshaping.hpp" # include # include # include @@ -211,16 +216,37 @@ 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)) + { + auto restr = nana::reshaping::arabic::reshape(std::wstring{e.begin, e.end}); + rtl.assign(restr.crbegin(), restr.crend()); - len -= preferred.second; - str += preferred.second; + 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; @@ -236,22 +262,28 @@ 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()); +#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(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) - 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; } @@ -265,21 +297,69 @@ 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()); +#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(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(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 +#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(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) { @@ -298,14 +378,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) @@ -359,10 +439,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; } 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/gui/animation.cpp b/source/gui/animation.cpp index f897735f..09861aaf 100644 --- a/source/gui/animation.cpp +++ b/source/gui/animation.cpp @@ -1,7 +1,7 @@ /* * An Animation Implementation * 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 @@ -40,6 +40,7 @@ namespace nana { drawing::diehard_t diehard{ nullptr }; std::vector points; + std::vector> areas; }; struct framebuilder @@ -184,7 +185,7 @@ namespace nana std::list frames; std::list::iterator this_frame; std::size_t pos_in_this_frame{ 0 }; - mutable bool good_frame_by_frmbuilder{ false }; //It indicates the state of frame whether is valid. + mutable bool good_frame_by_frmbuilder{ false }; //It indicates the state of frame. impl() : this_frame(frames.end()) @@ -200,52 +201,96 @@ namespace nana switch(frmobj.type) { case frame::kind::oneshot: - _m_render(outs, [&frmobj](paint::graphics& tar, const nana::point& pos) + _m_render(outs, frmobj.u.oneshot->size(), [&frmobj](paint::graphics& tar, const nana::rectangle& area) { - frmobj.u.oneshot->paste(tar, pos); + if(frmobj.u.oneshot->size() == area.dimension()) + frmobj.u.oneshot->paste(tar, area.position()); + else + frmobj.u.oneshot->stretch(rectangle{frmobj.u.oneshot->size()}, tar, area); }); break; case frame::kind::framebuilder: good_frame_by_frmbuilder = frmobj.u.frbuilder->frbuilder(pos_in_this_frame, framegraph, framegraph_dimension); if(good_frame_by_frmbuilder) { - nana::rectangle r(framegraph_dimension); - _m_render(outs, [&r, &framegraph](paint::graphics& tar, const nana::point& pos) mutable + _m_render(outs, framegraph_dimension, [framegraph_dimension, &framegraph](paint::graphics& tar, const rectangle& area) mutable { - r.x = pos.x; - r.y = pos.y; - tar.bitblt(r, framegraph); + if(framegraph_dimension == area.dimension()) + tar.bitblt(area, framegraph); + else + framegraph.stretch(tar, area); }); } break; } } - //Render a frame on a specified window graph - void render_this(paint::graphics& graph, const nana::point& pos, paint::graphics& framegraph, nana::size& framegraph_dimension, bool rebuild_frame) const + //Render a frame on a specified window graph. If this frame is created by framebuilder, it doesn't rebuild the frame. + void render_this(paint::graphics& graph, const rectangle& area, paint::graphics& framegraph, nana::size& framegraph_dimension) const { - if(this_frame == frames.end()) - return; + // If the frame is EOF, then renders the last frame + std::list::const_iterator pf = this_frame; + if (pf == frames.end()) + { + if (frames.size()) + { + pf = frames.begin(); + std::advance(pf, frames.size() - 1); + } + else + return; + } - frame & frmobj = *this_frame; - switch(frmobj.type) + const frame & frmobj = *pf; + switch (frmobj.type) { case frame::kind::oneshot: - frmobj.u.oneshot->paste(graph, pos); + if (frmobj.u.oneshot->size() == area.dimension()) + frmobj.u.oneshot->paste(graph, area.position()); + else + frmobj.u.oneshot->stretch(rectangle{frmobj.u.oneshot->size()}, graph, area); break; case frame::kind::framebuilder: - if(rebuild_frame) - good_frame_by_frmbuilder = frmobj.u.frbuilder->frbuilder(pos_in_this_frame, framegraph, framegraph_dimension); - if(good_frame_by_frmbuilder) { - nana::rectangle r(pos, framegraph_dimension); - graph.bitblt(r, framegraph); + if (framegraph_dimension == area.dimension()) + graph.bitblt(area, framegraph); + else + framegraph.stretch(graph, area); } break; } } + nana::size this_frame_size(const nana::size& framegraph_dimension) const + { + // If the frame is EOF, then renders the last frame + std::list::const_iterator pf = this_frame; + if (pf == frames.end()) + { + if (frames.size()) + { + pf = frames.begin(); + std::advance(pf, frames.size() - 1); + } + else + return{}; + } + + const frame & frmobj = *pf; + switch (frmobj.type) + { + case frame::kind::oneshot: + return frmobj.u.oneshot->size(); + case frame::kind::framebuilder: + if (good_frame_by_frmbuilder) + return framegraph_dimension; + break; + } + + return{}; + } + bool eof() const { return (frames.end() == this_frame); @@ -285,8 +330,10 @@ namespace nana } private: template - void _m_render(std::map& outs, Renderer renderer) const + void _m_render(std::map& outs, const nana::size& frame_size, Renderer renderer) const { + nana::rectangle frame_area{frame_size}; + for(auto & tar: outs) { auto graph = API::dev::window_graphics(tar.first); @@ -294,7 +341,13 @@ namespace nana continue; for(auto & outp : tar.second.points) - renderer(*graph, outp); + { + frame_area.position(outp); + renderer(*graph, frame_area); + } + + for(auto& area_fn: tar.second.areas) + renderer(*graph, area_fn()); API::update_window(tar.first); } @@ -302,7 +355,7 @@ namespace nana };//end struct frameset::impl //public: frameset::frameset() - : impl_(new impl) + : impl_(std::make_unique()) {} void frameset::push_back(paint::image img) @@ -339,6 +392,8 @@ namespace nana double performance_parameter; }; + ~performance_manager(); + void insert(impl* p); void set_fps(impl*, std::size_t new_fps); void close(impl* p); @@ -400,19 +455,32 @@ namespace nana } } - void render_this_specifically(paint::graphics& graph, const nana::point& pos) + // Renders current frame to a specified graphics + void render_this_frame(paint::graphics& graph, const rectangle& area) { if(state.this_frameset != framesets.end()) - state.this_frameset->impl_->render_this(graph, pos, framegraph, framegraph_dimension, false); + state.this_frameset->impl_->render_this(graph, area, framegraph, framegraph_dimension); } + // Renders current from to all outputs graphics void render_this_frame() { if(state.this_frameset != framesets.end()) state.this_frameset->impl_->render_this(outputs, framegraph, framegraph_dimension); } - bool move_to_next() + nana::size this_frame_size() const + { + if (state.this_frameset != framesets.end()) + { + return state.this_frameset->impl_->this_frame_size(framegraph_dimension); + } + return{}; + } + + + + bool next_frame() { if(state.this_frameset != framesets.end()) { @@ -429,9 +497,28 @@ namespace nana if(state.this_frameset != framesets.end()) state.this_frameset->impl_->reset(); } + + bool eof() const + { + if(state.this_frameset != framesets.end()) + return state.this_frameset->impl_->eof(); + + return true; + } };//end struct animation::impl //class animation::performance_manager + animation::performance_manager::~performance_manager() + { + for (auto thr : threads_) + { + if (thr->thread && thr->thread->joinable()) + thr->thread->join(); + + delete thr; + } + } + void animation::performance_manager::insert(impl* p) { std::lock_guard lock(mutex_); @@ -449,20 +536,26 @@ namespace nana } } - auto thr = new thread_variable; + auto thr = std::make_unique(); thr->animations.push_back(p); thr->performance_parameter = 0.0; thr->fps = p->fps; thr->interval = 1000.0 / double(p->fps); - thr->thread = std::make_shared([thr]() + auto pthr = thr.get(); + thr->thread = std::make_shared([pthr]() { + auto thr = pthr; nana::system::timepiece tmpiece; + tmpiece.start(); + while (true) { thr->active = 0; - tmpiece.start(); { + //acquire the isg lock first to avoid deadlock that occured by an event hander which operates the animation object. + nana::internal_scope_guard isglock; + std::lock_guardmutex)> lock(thr->mutex); for (auto ani : thr->animations) { @@ -470,7 +563,25 @@ namespace nana continue; ani->render_this_frame(); - if (false == ani->move_to_next()) + } + } + + thr->performance_parameter = tmpiece.calc(); + if (thr->performance_parameter < thr->interval) + nana::system::sleep(static_cast(thr->interval - thr->performance_parameter)); + + //Restart timing this frame + tmpiece.start(); + + // Move to next frame + { + std::lock_guardmutex)> lock(thr->mutex); + for (auto ani : thr->animations) + { + if (ani->paused) + continue; + + if (false == ani->next_frame()) { if (ani->looped) { @@ -483,25 +594,31 @@ namespace nana } } - if (thr->active) - { - thr->performance_parameter = tmpiece.calc(); - if (thr->performance_parameter < thr->interval) - nana::system::sleep(static_cast(thr->interval - thr->performance_parameter)); - } - else + if (0 == thr->active) { //There isn't an active frame, then let the thread //wait for a signal for an active animation std::unique_lock lock(thr->mutex); + + //Exit the thread if there is not an animation + if (thr->animations.empty()) + return; + if (0 == thr->active) thr->condvar.wait(lock); + + //Exit the thread if there is not an animation + if (thr->animations.empty()) + return; + + //Restart timing for this frame when this thread is waking up. + tmpiece.start(); } } }); - threads_.push_back(thr); - p->thr_variable = thr; + threads_.push_back(thr.release()); + p->thr_variable = threads_.back(); } void animation::performance_manager::set_fps(impl* p, std::size_t new_fps) @@ -525,10 +642,13 @@ namespace nana return; } - std::lock_guardmutex)> privlock(thr->mutex); - auto u = std::find(thr->animations.begin(), thr->animations.end(), p); - if (u != thr->animations.end()) - thr->animations.erase(u); + { + // the mutex of thread variable may be acquired by insert() + std::lock_guardmutex)> privlock(thr->mutex); + auto u = std::find(thr->animations.begin(), thr->animations.end(), p); + if (u != thr->animations.end()) + thr->animations.erase(u); + } p->thr_variable = nullptr; insert(p); @@ -542,11 +662,30 @@ namespace nana return; auto thr = *i; - std::lock_guardmutex)> privlock(thr->mutex); - auto u = std::find(thr->animations.begin(), thr->animations.end(), p); - if(u != thr->animations.end()) - thr->animations.erase(u); + { + std::lock_guardmutex)> privlock(thr->mutex); + + auto u = std::find(thr->animations.begin(), thr->animations.end(), p); + if (u != thr->animations.end()) + thr->animations.erase(u); + + //If there is not an animation in the thread, wake up the thread to exit. + //If there is an animation in the thread, set the thr pointer to nullptr to + //avoid exiting the thread + if (thr->animations.empty()) + thr->condvar.notify_one(); + else + thr = nullptr; + } + + p->thr_variable = nullptr; + + threads_.erase(i); + if (thr && thr->thread && thr->thread->joinable()) + thr->thread->join(); + + delete thr; } bool animation::performance_manager::empty() const @@ -562,30 +701,23 @@ namespace nana //end class animation::performance_manager animation::animation(std::size_t fps) - : impl_(new impl(fps)) + : impl_(std::make_unique(fps)) { } - animation::~animation() - { - delete impl_; - } + animation::~animation() = default; animation::animation(animation&& rhs) - : impl_(rhs.impl_) + : impl_(std::move(rhs.impl_)) { - rhs.impl_ = new impl(23); + rhs.impl_ = std::make_unique(23); } animation& animation::operator=(animation&& rhs) { if (this != &rhs) { - auto imp = new impl{ 23 }; - - delete impl_; - impl_ = rhs.impl_; - rhs.impl_ = imp; + std::swap(rhs.impl_, this->impl_); } return *this; } @@ -633,6 +765,9 @@ namespace nana std::unique_lock lock(impl_->thr_variable->mutex); if(0 == impl_->thr_variable->active) { + if (impl_->eof()) + impl_->reset(); + impl_->thr_variable->active = 1; impl_->thr_variable->condvar.notify_one(); } @@ -651,7 +786,7 @@ namespace nana { drawing dw(wd); output.diehard = dw.draw_diehard([this, pos](paint::graphics& tar){ - impl_->render_this_specifically(tar, pos); + impl_->render_this_frame(tar, rectangle{ pos, impl_->this_frame_size() }); }); API::events(wd).destroy.connect([this](const arg_destroy& arg){ @@ -662,12 +797,31 @@ namespace nana output.points.push_back(pos); } + void animation::output(window wd, std::function r) + { + auto & output = impl_->outputs[wd]; + + if(nullptr == output.diehard) + { + drawing dw(wd); + output.diehard = dw.draw_diehard([this, r](paint::graphics& tar){ + impl_->render_this_frame(tar, r()); + }); + + API::events(wd).destroy.connect([this](const arg_destroy& arg){ + std::lock_guardthr_variable->mutex)> lock(impl_->thr_variable->mutex); + impl_->outputs.erase(arg.window_handle); + }); + } + output.areas.push_back(r); + } + void animation::fps(std::size_t n) { if (n == impl_->fps) return; - impl::perf_manager->set_fps(impl_, n); + impl::perf_manager->set_fps(impl_.get(), n); } std::size_t animation::fps() const 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); } } 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) { 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) }; diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index 7d8c4475..472efc06 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 @@ -1637,16 +1637,16 @@ namespace nana { - if (select_.a.x < line.size() && !std::isalnum(line[select_.a.x]) && line[select_.a.x] != '_') { + if (select_.a.x < line.size() && !std::iswalnum(line[select_.a.x]) && line[select_.a.x] != '_') { ++select_.b.x; } else { // Expand the selection forward to the word's end. - while (select_.b.x < line.size() && !std::iswspace(line[select_.b.x]) && (std::isalnum(line[select_.b.x]) || line[select_.b.x] == '_')) + while (select_.b.x < line.size() && !std::iswspace(line[select_.b.x]) && (std::iswalnum(line[select_.b.x]) || line[select_.b.x] == '_')) ++select_.b.x; // Expand the selection backward to the word's start. - while (select_.a.x > 0 && !std::iswspace(line[select_.a.x - 1]) && (std::isalnum(line[select_.a.x - 1]) || line[select_.a.x - 1] == '_')) + while (select_.a.x > 0 && !std::iswspace(line[select_.a.x - 1]) && (std::iswalnum(line[select_.a.x - 1]) || line[select_.a.x - 1] == '_')) --select_.a.x; } select_.mode_selection = selection::mode::method_selected; @@ -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) { @@ -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) { - 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)); diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 0a114471..6bb7bed8 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) @@ -2465,6 +2465,12 @@ namespace nana dw->impl()->use_entire_line = enable; } + auto treebox::first() const -> item_proxy + { + auto impl = get_drawer_trigger().impl(); + return item_proxy{ const_cast(&get_drawer_trigger()), impl->attr.tree_cont.get_root()->child }; + } + std::shared_ptr treebox::_m_scroll_operation() { internal_scope_guard lock; diff --git a/source/paint/detail/image_bmp.hpp b/source/paint/detail/image_bmp.hpp index ac25812e..0e427285 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 @@ -14,6 +14,7 @@ #define NANA_PAINT_DETAIL_IMAGE_BMP_HPP #include +#include #include "image_pixbuf.hpp" namespace nana{ namespace paint @@ -106,7 +107,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 +133,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 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 +218,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 = 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 +249,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) diff --git a/source/paint/detail/image_jpeg.hpp b/source/paint/detail/image_jpeg.hpp index 0b1ecd2c..95522530 100644 --- a/source/paint/detail/image_jpeg.hpp +++ b/source/paint/detail/image_jpeg.hpp @@ -3,7 +3,7 @@ #include "image_pixbuf.hpp" -//Separate the libpng from the package that system provides. +//Separate the libjpeg from the package that system provides. #if defined(NANA_LIBJPEG) #include #else 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); } diff --git a/source/paint/pixel_buffer.cpp b/source/paint/pixel_buffer.cpp index 304bbc33..6f1fc9bd 100644 --- a/source/paint/pixel_buffer.cpp +++ b/source/paint/pixel_buffer.cpp @@ -1,7 +1,7 @@ /* * Pixel Buffer 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 @@ -190,41 +190,42 @@ namespace nana{ namespace paint if (!raw_pixel_buffer) return; + if ((32 == bits_per_pixel) && (pixel_size.width == width) && (pixel_size.height == height) && (this->bytes_per_line == bytes_per_line) && is_negative) + { + memcpy(raw_pixel_buffer, rawbits, (bytes_per_line * pixel_size.height)); + return; + } + + + if(pixel_size.width < width) + width = pixel_size.width; + + if(pixel_size.height < height) + height = pixel_size.height; + auto rawptr = raw_pixel_buffer; if(32 == bits_per_pixel) { - if((pixel_size.width == width) && (pixel_size.height == height) && is_negative) + auto d = rawptr; + const unsigned char* s; + int src_line_bytes; + + if (is_negative) { - memcpy(rawptr, rawbits, (pixel_size.width * pixel_size.height) * 4); + s = rawbits; + src_line_bytes = -static_cast(bytes_per_line); } else { - std::size_t line_bytes = (pixel_size.width < width ? pixel_size.width : width) * sizeof(pixel_color_t); + s = rawbits + bytes_per_line * (height - 1); + src_line_bytes = static_cast(bytes_per_line); + } - if(pixel_size.height < height) - height = pixel_size.height; - - auto d = rawptr; - const unsigned char* s; - int src_line_bytes; - - if (is_negative) - { - s = rawbits; - src_line_bytes = -static_cast(bytes_per_line); - } - else - { - s = rawbits + bytes_per_line * (height - 1); - src_line_bytes = static_cast(bytes_per_line); - } - - for(std::size_t i = 0; i < height; ++i) - { - memcpy(d, s, line_bytes); - d += pixel_size.width; - s -= src_line_bytes; - } + for(std::size_t i = 0; i < height; ++i) + { + memcpy(d, s, this->bytes_per_line); + d += pixel_size.width; + s -= src_line_bytes; } } else if(24 == bits_per_pixel) @@ -269,12 +270,6 @@ namespace nana{ namespace paint } else if(16 == bits_per_pixel) { - if(pixel_size.width < width) - width = pixel_size.width; - - if(pixel_size.height < height) - height = pixel_size.height; - unsigned char rgb_table[32]; for(std::size_t i =0; i < 32; ++i) rgb_table[i] = static_cast(i * 255 / 31); @@ -310,6 +305,32 @@ namespace nana{ namespace paint rawbits -= src_bytes_per_line; } } + else if(8 == bits_per_pixel) + { + int src_bytes_per_line; + if(!is_negative) + { + rawbits += bytes_per_line * (height - 1); + src_bytes_per_line = -static_cast(bytes_per_line); + } + else + src_bytes_per_line = static_cast(bytes_per_line); + + for(std::size_t top = 0; top < height; ++top) + { + auto dst = rawptr; + for(auto p = rawbits, end = rawbits + width; p < end; ++p) + { + dst->element.red = *p; + dst->element.green = *p; + dst->element.blue = *p; + ++dst; + } + + rawbits += src_bytes_per_line; + rawptr += this->bytes_per_line; + } + } } #if defined(NANA_X11) @@ -691,6 +712,17 @@ namespace nana{ namespace paint ++px; } } + else if(8 == bits_per_pixel) + { + //Grayscale + for (auto p = row_ptr, end = row_ptr + px_count; p != end; ++p) + { + p->element.red = *buffer; + p->element.green = *buffer; + p->element.blue = *buffer; + ++buffer; + } + } } diff --git a/source/system/platform.cpp b/source/system/platform.cpp index d8832c68..83e803a0 100644 --- a/source/system/platform.cpp +++ b/source/system/platform.cpp @@ -18,46 +18,53 @@ #include #include "../detail/mswin/platform_spec.hpp" #elif defined(NANA_POSIX) - #include - #include + #include "split_string.hpp" #include #include #include #include #include #include - #include + #include + #include static void posix_open_url(const char *url_utf8) { + using nana::system::split_string_type; + using nana::system::split_string; + extern char **environ; - const char *home = getenv("HOME"); - std::string cheat(home); - cheat += "/.mozilla"; struct stat exists; + const split_string_type path = getenv("PATH"); - // TODO: generalize this for chromium, opera, waterfox, etc. - // Most desktop environments (KDE, Gnome, Lumina etc.) provide a way to set - // your preferred browser - but there are more desktops than browsers. + //see https://stackoverflow.com/questions/5116473/linux-command-to-open-url-in-default-browser + + std::string full_path; + for (const auto& cur_path : split_string(path, ':')) { + full_path = cur_path; + full_path += "/xdg-open"; + if ( stat(full_path.c_str(), &exists) == 0 && S_ISREG(exists.st_mode)) + break; + else + full_path.clear(); + } + if (full_path.empty()) { + //xdg-open not found sorry :( maybe print a message so users know? + return; + } - // Look for $HOME/.mozilla directory as strong evidence they use firefox. - if ( stat(cheat.c_str(), &exists) == 0 && S_ISDIR(exists.st_mode)) { - const char *path = ""; - static const char *likely[2] = { "/usr/local/bin/firefox", "/usr/bin/firefox"}; - if ( stat(likely[0], &exists) == 0 && S_ISREG(exists.st_mode)) - path = likely[0]; - else if ( stat(likely[1], &exists) == 0 && S_ISREG(exists.st_mode) ) - path = likely[1]; - else return; - pid_t pid = 0; - static const char firefox[] = "firefox"; - char name[sizeof firefox]{}; // argv does not like const-literals so make a copy. - strcpy(name, firefox); - char *argv[3] = {name, const_cast(url_utf8), nullptr}; - posix_spawn(&pid, path, NULL, NULL, argv, environ); + const auto url_utf8_len = std::strlen(url_utf8); + auto string_cpy_buff = std::make_unique(full_path.size() + 1 + url_utf8_len + 1); + char* const url_utf8_cpy = string_cpy_buff.get(); + char* const full_path_cpy = string_cpy_buff.get() + url_utf8_len + 1; + std::strncpy(url_utf8_cpy, url_utf8, url_utf8_len + 1); + std::strncpy(full_path_cpy, full_path.c_str(), full_path.size() + 1); + char *argv[3] = {full_path_cpy, url_utf8_cpy, nullptr}; + //system((full_path + " " + url_utf8).c_str()); + posix_spawn(&pid, full_path_cpy, nullptr, nullptr, argv, environ); } } #endif diff --git a/source/system/split_string.cpp b/source/system/split_string.cpp new file mode 100644 index 00000000..8269db6d --- /dev/null +++ b/source/system/split_string.cpp @@ -0,0 +1,32 @@ +#include "split_string.hpp" +#include +#include +#include + +namespace nana +{ +namespace system { +std::vector split_string (const split_string_type& text, char sep) +{ + std::vector retval; + const auto estimated_size = std::count(text.begin(), text.end(), sep) + 1; + retval.reserve(estimated_size); + + std::size_t sep_pos = 0; + while (sep_pos != text.size()) { + const std::size_t start = sep_pos; + sep_pos = text.find(sep, sep_pos); + sep_pos = (text.npos == sep_pos ? text.size() : sep_pos); + const std::size_t end = sep_pos; + while (sep_pos < text.size() and sep == text[sep_pos]) { + ++sep_pos; + } + + retval.push_back(text.substr(start, end - start)); + } + + return retval; +} + +} +} diff --git a/source/system/split_string.hpp b/source/system/split_string.hpp new file mode 100644 index 00000000..1512ef41 --- /dev/null +++ b/source/system/split_string.hpp @@ -0,0 +1,41 @@ +/* + * The Deploy Implementation + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * @file: nana/system/split_string.hpp + * + * What follows is dependent on what defined in nana/config.hpp + */ + +#ifndef NANA_SYSTEM_SPLITSTRING_HPP +#define NANA_SYSTEM_SPLITSTRING_HPP + +#include +#include +#ifdef _nana_std_has_string_view +# include +#else +# include +#endif + +namespace nana +{ +namespace system +{ +#ifdef _nana_std_has_string_view +typedef std::string_view split_string_type; +#else +typedef std::string split_string_type; +#endif + +std::vector split_string (const split_string_type& text, char sep); + +} +} + +#endif diff --git a/source/unicode_bidi.cpp b/source/unicode_bidi.cpp index f71645c5..5957981e 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 @@ -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) @@ -990,7 +995,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 +1036,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; }