diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index 83668943..f14c043f 100644 --- a/include/nana/filesystem/filesystem.hpp +++ b/include/nana/filesystem/filesystem.hpp @@ -428,7 +428,7 @@ namespace nana { namespace experimental { namespace filesystem file_status status(const path& p, std::error_code&); std::uintmax_t file_size(const path& p); - //uintmax_t file_size(const path& p, error_code& ec) noexcept; + std::uintmax_t file_size(const path& p, std::error_code& ec) noexcept; inline bool is_directory(file_status s) noexcept { return s.type() == file_type::directory ;} diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index 12b23ced..89b3651b 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -1,7 +1,7 @@ /* * A text editor implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -66,6 +66,7 @@ namespace nana{ namespace widgets using char_type = wchar_t; using size_type = textbase::size_type; using string_type = textbase::string_type; + using path_type = std::filesystem::path; using event_interface = text_editor_event_interface; @@ -105,7 +106,7 @@ namespace nana{ namespace widgets void indent(bool, std::function generator); void set_event(event_interface*); - bool load(const char*); + bool load(const path_type& file); void text_align(::nana::align alignment); diff --git a/include/nana/gui/widgets/skeletons/textbase.hpp b/include/nana/gui/widgets/skeletons/textbase.hpp index d11bc872..130b1466 100644 --- a/include/nana/gui/widgets/skeletons/textbase.hpp +++ b/include/nana/gui/widgets/skeletons/textbase.hpp @@ -1,7 +1,7 @@ /* * A textbase class implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -18,10 +18,11 @@ #include #include #include +#include + #include "textbase_export_interface.hpp" #include -#include #include #include @@ -36,15 +37,16 @@ namespace skeletons : public ::nana::noncopyable { public: - typedef CharT char_type; - typedef std::basic_string string_type; - typedef typename string_type::size_type size_type; + using char_type = CharT; + using string_type = std::basic_string; + using size_type = typename string_type::size_type; + using path_type = std::filesystem::path; textbase() { attr_max_.reset(); //Insert an empty string for the first line of empty text. - text_cont_.emplace_back(new string_type); + text_cont_.emplace_back(); } void set_event_agent(textbase_event_agent_interface * evt) @@ -55,21 +57,19 @@ namespace skeletons bool empty() const { return (text_cont_.empty() || - ((text_cont_.size() == 1) && (text_cont_.front()->empty()))); + ((text_cont_.size() == 1) && text_cont_.front().empty())); } - bool load(const char* file_utf8) + bool load(const path_type& file) { - if (!file_utf8) - return false; - - std::ifstream ifs(to_osmbstr(file_utf8)); + std::ifstream ifs{ file.string() }; if (!ifs) return false; - ifs.seekg(0, std::ios::end); - std::size_t bytes = static_cast(ifs.tellg()); - ifs.seekg(0, std::ios::beg); + std::error_code err; + auto const bytes = file_size(file, err); + if (err) + return false; if(bytes >= 2) { @@ -81,7 +81,7 @@ namespace skeletons if(0xBB == ch && 0xBF == ifs.get()) { ifs.close(); - return load(file_utf8, nana::unicode::utf8); + return load(file, nana::unicode::utf8); } } else if(0xFF == ch) @@ -89,16 +89,13 @@ namespace skeletons if(0xFE == ifs.get()) { //UTF16,UTF32 - if(bytes >= 4) + if((bytes >= 4) && (ifs.get() == 0 && ifs.get() == 0)) { - if(ifs.get() == 0 && ifs.get() == 0) - { - ifs.close(); - return load(file_utf8, nana::unicode::utf32); - } + ifs.close(); + return load(file, nana::unicode::utf32); } ifs.close(); - return load(file_utf8, nana::unicode::utf16); + return load(file, nana::unicode::utf16); } } else if(0xFE == ch) @@ -107,7 +104,7 @@ namespace skeletons { //UTF16(big-endian) ifs.close(); - return load(file_utf8, nana::unicode::utf16); + return load(file, nana::unicode::utf16); } } else if(0 == ch) @@ -119,7 +116,7 @@ namespace skeletons { //UTF32(big_endian) ifs.close(); - return load(file_utf8, nana::unicode::utf32); + return load(file, nana::unicode::utf32); } } } @@ -135,15 +132,15 @@ namespace skeletons while(ifs.good()) { std::getline(ifs, str_mbs); - text_cont_.emplace_back(new string_type(static_cast(nana::charset{ str_mbs }))); - if(text_cont_.back()->size() > attr_max_.size) + text_cont_.emplace_back(static_cast(nana::charset{ str_mbs })); + if (text_cont_.back().size() > attr_max_.size) { - attr_max_.size = text_cont_.back()->size(); + attr_max_.size = text_cont_.back().size(); attr_max_.line = text_cont_.size() - 1; } } - _m_saved(file_utf8); + _m_saved(file); return true; } @@ -175,12 +172,9 @@ namespace skeletons } } - bool load(const char* file_utf8, nana::unicode encoding) + bool load(const path_type& file, nana::unicode encoding) { - if (!file_utf8) - return false; - - std::ifstream ifs(to_osmbstr(file_utf8)); + std::ifstream ifs{ file.string() }; if (!ifs) return false; @@ -218,9 +212,8 @@ namespace skeletons byte_order_translate_4bytes(str); } - text_cont_.emplace_back(new string_type(static_cast(nana::charset{ str, encoding }))); - - attr_max_.size = text_cont_.back()->size(); + text_cont_.emplace_back(static_cast(nana::charset{ str, encoding })); + attr_max_.size = text_cont_.back().size(); attr_max_.line = 0; } @@ -236,21 +229,21 @@ namespace skeletons byte_order_translate_4bytes(str); } - text_cont_.emplace_back(new string_type(static_cast(nana::charset{ str, encoding }))); - if(text_cont_.back()->size() > attr_max_.size) + text_cont_.emplace_back(static_cast(nana::charset{ str, encoding })); + if (text_cont_.back().size() > attr_max_.size) { - attr_max_.size = text_cont_.back()->size(); + attr_max_.size = text_cont_.back().size(); attr_max_.line = text_cont_.size() - 1; } } - _m_saved(file_utf8); + _m_saved(file); return true; } - void store(std::string fs, bool is_unicode, ::nana::unicode encoding) const + void store(const path_type& filename, bool is_unicode, ::nana::unicode encoding) const { - std::ofstream ofs(to_osmbstr(fs), std::ios::binary); + std::ofstream ofs(filename.string(), std::ios::binary); if(ofs && text_cont_.size()) { auto i = text_cont_.cbegin(); @@ -277,27 +270,26 @@ namespace skeletons for (std::size_t pos = 0; pos < count; ++pos) { - auto mbs = nana::charset(**(i++)).to_bytes(encoding); + auto mbs = nana::charset(*(i++)).to_bytes(encoding); ofs.write(mbs.c_str(), static_cast(mbs.size())); ofs.write("\r\n", 2); } - last_mbs = nana::charset(*text_cont_.back()).to_bytes(encoding); + last_mbs = nana::charset(text_cont_.back()).to_bytes(encoding); } else { for (std::size_t pos = 0; pos < count; ++pos) { - std::string mbs = nana::charset(**(i++)); + std::string mbs = nana::charset(*(i++)); ofs.write(mbs.c_str(), mbs.size()); ofs.write("\r\n", 2); } - - last_mbs = nana::charset(*text_cont_.back()); + last_mbs = nana::charset(text_cont_.back()); } ofs.write(last_mbs.c_str(), static_cast(last_mbs.size())); - _m_saved(std::move(fs)); + _m_saved(filename); } } @@ -311,7 +303,7 @@ namespace skeletons { if (!changed_) { - _m_first_change(); + _m_emit_first_change(); changed_ = true; } @@ -332,8 +324,7 @@ namespace skeletons const string_type& getline(size_type pos) const { if (pos < text_cont_.size()) - return *text_cont_[pos]; - + return text_cont_[pos]; return nullstr_; } @@ -346,7 +337,7 @@ namespace skeletons { if (text_cont_.size() <= pos) { - text_cont_.emplace_back(new string_type(std::move(text))); + text_cont_.emplace_back(std::move(text)); pos = text_cont_.size() - 1; } else @@ -369,7 +360,7 @@ namespace skeletons } else { - text_cont_.emplace_back(new string_type(std::move(str))); + text_cont_.emplace_back(std::move(str)); pos.y = static_cast(text_cont_.size() - 1); } @@ -380,9 +371,9 @@ namespace skeletons void insertln(size_type pos, string_type&& str) { if (pos < text_cont_.size()) - text_cont_.emplace(_m_iat(pos), new string_type(std::move(str))); + text_cont_.emplace(_m_iat(pos), std::move(str)); else - text_cont_.emplace_back(new string_type(std::move(str))); + text_cont_.emplace_back(std::move(str)); _m_make_max(pos); edited_ = true; @@ -429,9 +420,9 @@ namespace skeletons { text_cont_.clear(); attr_max_.reset(); - text_cont_.emplace_back(new string_type); //text_cont_ must not be empty + text_cont_.emplace_back(); //text_cont_ must not be empty - _m_saved(std::string()); + _m_saved({}); } void merge(size_type pos) @@ -439,7 +430,8 @@ namespace skeletons if(pos + 1 < text_cont_.size()) { auto i = _m_iat(pos + 1); - _m_at(pos) += **i; + _m_at(pos) += *i; + text_cont_.erase(i); _m_make_max(pos); @@ -453,7 +445,7 @@ namespace skeletons } } - const std::string& filename() const + const path_type& filename() const { return filename_; } @@ -463,33 +455,25 @@ namespace skeletons return changed_; } - void edited_reset() + void reset_status(bool remain_saved_filename) { - changed_ = false; - } + if(!remain_saved_filename) + filename_.clear(); - void reset() - { - filename_.clear(); changed_ = false; } bool saved() const { - return ! not_saved(); - } - - bool not_saved() const - { - return edited() || filename_.empty(); + return !(changed_ || filename_.empty()); } private: string_type& _m_at(size_type pos) { - return **_m_iat(pos); + return *_m_iat(pos); } - typename std::deque>::iterator _m_iat(size_type pos) + typename std::deque::iterator _m_iat(size_type pos) { return text_cont_.begin() + pos; } @@ -506,50 +490,40 @@ namespace skeletons void _m_scan_for_max() { - attr_max_.size = 0; - std::size_t n = 0; - for(auto & p : text_cont_) - { - if(p->size() > attr_max_.size) - { - attr_max_.size = p->size(); - attr_max_.line = n; - } - ++n; - } + attr_max_.reset(); + for (std::size_t i = 0; i < text_cont_.size(); ++i) + _m_make_max(i); } - void _m_first_change() const + void _m_emit_first_change() const { if (evt_agent_) evt_agent_->first_change(); } - void _m_saved(std::string && filename) const + void _m_saved(const path_type& filename) const { - if(filename_ != filename) + if((filename_ != filename) || changed_) { - filename_ = std::move(filename); - _m_first_change(); + filename_ = filename; + _m_emit_first_change(); } - else if(changed_) - _m_first_change(); changed_ = false; } private: - std::deque> text_cont_; + std::deque text_cont_; textbase_event_agent_interface* evt_agent_{ nullptr }; mutable bool changed_{ false }; mutable bool edited_{ false }; - mutable std::string filename_; //A string for the saved filename. + mutable path_type filename_; ///< The saved filename const string_type nullstr_; struct attr_max { - std::size_t line; - std::size_t size; + std::size_t line; ///< The line number of max line + std::size_t size; ///< The number of characters in max line void reset() { diff --git a/include/nana/gui/widgets/textbox.hpp b/include/nana/gui/widgets/textbox.hpp index 5f245761..8bf34b38 100644 --- a/include/nana/gui/widgets/textbox.hpp +++ b/include/nana/gui/widgets/textbox.hpp @@ -1,7 +1,7 @@ /** * A Textbox Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -109,6 +109,8 @@ namespace nana using text_focus_behavior = widgets::skeletons::text_focus_behavior; using text_positions = std::vector; + using path_type = std::filesystem::path; + /// The default constructor without creating the widget. textbox(); @@ -136,9 +138,9 @@ namespace nana textbox(window, const rectangle& = rectangle(), bool visible = true); /// \brief Loads a text file. When attempt to load a unicode encoded text file, be sure the file have a BOM header. - void load(std::string file); - void store(std::string file); - void store(std::string file, nana::unicode encoding); + void load(const path_type& file); + void store(const path_type& file); + void store(const path_type& file, nana::unicode encoding); colored_area_access_interface* colored_area_access(); @@ -158,7 +160,7 @@ namespace nana textbox& reset(const std::string& text = std::string(), bool end_caret = true); ///< discard the old text and set a new text /// The file of last store operation. - std::string filename() const; + path_type filename() const; /// Determine whether the text was edited. bool edited() const; diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp index 4ec85bf5..e1a59d80 100644 --- a/source/filesystem/filesystem.cpp +++ b/source/filesystem/filesystem.cpp @@ -936,7 +936,7 @@ namespace nana { namespace experimental { namespace filesystem auto stat = status(p, err); if (err != std::error_code()) - throw filesystem_error("nana::experimental::filesystem::status", p, err); + throw filesystem_error("nana::filesystem::status", p, err); return stat; } @@ -1002,6 +1002,16 @@ namespace nana { namespace experimental { namespace filesystem std::uintmax_t file_size(const path& p) { + std::error_code err; + auto bytes = file_size(p, err); + if (err) + throw filesystem_error("nana::filesystem::status", p, err); + + return bytes; + } + + std::uintmax_t file_size(const path& p, std::error_code& ec) + { #if defined(NANA_WINDOWS) //Some compilation environment may fail to link to GetFileSizeEx typedef BOOL(__stdcall *GetFileSizeEx_fptr_t)(HANDLE, PLARGE_INTEGER); @@ -1019,23 +1029,25 @@ namespace nana { namespace experimental { namespace filesystem return li.QuadPart; } } - return 0; + ec.assign(static_cast(::GetLastError()), std::generic_category()); #elif defined(NANA_POSIX) FILE * stream = ::fopen(p.c_str(), "rb"); - long long size = 0; if (stream) { + long long bytes = 0; # if defined(NANA_LINUX) fseeko64(stream, 0, SEEK_END); - size = ftello64(stream); + bytes = ftello64(stream); # elif defined(NANA_POSIX) fseeko(stream, 0, SEEK_END); - size = ftello(stream); + bytes = ftello(stream); # endif ::fclose(stream); + return bytes; } - return size; + ec.assign(static_cast(::errno), std::generic_category()); #endif + return static_cast(-1); } diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index b0c4c800..f5af56a6 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -1275,7 +1275,7 @@ namespace nana { event_handler_ = ptr; } - bool text_editor::load(const char* fs) + bool text_editor::load(const path_type& fs) { if (!impl_->textbase.load(fs)) return false; diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index 8695f1e5..6e0a0ecc 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -1,7 +1,7 @@ /* * A Textbox Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -233,31 +233,31 @@ namespace drawerbase { create(wd, r, visible); } - void textbox::load(std::string file) + void textbox::load(const std::filesystem::path& file) { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); - if (editor && editor->load(file.data())) + if (editor && editor->load(file)) { if (editor->try_refresh()) API::update_window(handle()); } } - void textbox::store(std::string file) + void textbox::store(const std::filesystem::path& file) { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); if (editor) - editor->textbase().store(std::move(file), false, nana::unicode::utf8); //3rd parameter is just for syntax, it will be ignored + editor->textbase().store(file, false, nana::unicode::utf8); //3rd parameter is just for syntax, it will be ignored } - void textbox::store(std::string file, nana::unicode encoding) + void textbox::store(const std::filesystem::path& file, nana::unicode encoding) { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); if (editor) - editor->textbase().store(std::move(file), true, encoding); + editor->textbase().store(file, true, encoding); } textbox::colored_area_access_interface* textbox::colored_area_access() @@ -300,7 +300,8 @@ namespace drawerbase { if (end_caret) editor->move_caret_end(true); - editor->textbase().reset(); + //Reset the edited status and the saved filename + editor->textbase().reset_status(false); if (editor->try_refresh()) API::update_window(this->handle()); @@ -308,7 +309,7 @@ namespace drawerbase { return *this; } - std::string textbox::filename() const + textbox::path_type textbox::filename() const { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); @@ -330,7 +331,7 @@ namespace drawerbase { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); if (editor) - editor->textbase().edited_reset(); + editor->textbase().reset_status(true); return *this; }