From 5b1891c18657baef4d319dc356fdfe1111aef980 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 3 Jun 2016 08:00:19 +0800 Subject: [PATCH] listbox associative category access --- include/nana/gui/widgets/listbox.hpp | 140 +++++++++++++++++++------- include/nana/key_type.hpp | 4 +- source/gui/widgets/combox.cpp | 4 +- source/gui/widgets/listbox.cpp | 143 ++++++++++++++++++--------- 4 files changed, 208 insertions(+), 83 deletions(-) diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index a9e7f423..9973c67a 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -610,8 +610,93 @@ By \a clicking on one header the list get \a reordered, first up, and then down listbox(window, bool visible); listbox(window, const rectangle& = {}, bool visible = true); + //Element access + + /// Returns the category at specified location pos, with bounds checking. + cat_proxy at(size_type pos); + const cat_proxy at(size_type pos) const; + + /// Returns the item at specified absolute position + item_proxy at(const index_pair& abs_pos); + const item_proxy at(const index_pair &abs_pos) const; + + /// Returns the category at specified location pos, no bounds checking is performed. + cat_proxy operator[](size_type pos); + const cat_proxy operator[](size_type pos) const; + + /// Returns the item at specified absolute position, no bounds checking is performed. + item_proxy operator[](const index_pair& abs_pos); + const item_proxy operator[](const index_pair &abs_pos) const; + + //Associative element access + + /// Returns a proxy to the category of the key or create a new one in the right order + /** + * @param key The key of category to find + * @return A category proxy + */ + template + cat_proxy assoc(Key&& key) + { + using key_type = typename ::nana::detail::type_escape::type>::type; + + auto p = std::make_shared>>(std::forward(key)); + return cat_proxy(&_m_ess(), _m_assoc(p, true)); + } + + /// Returns a proxy to the category of the key or create a new one in the right order + /** + * @param key The key of category to find + * @return A category proxy + */ + template + cat_proxy assoc_at(Key&& key) + { + using key_type = typename ::nana::detail::type_escape::type>::type; + + auto p = std::make_shared>>(std::forward(key)); + + auto categ = _m_assoc(p, false); + if (nullptr == categ) + throw std::out_of_range("listbox: invalid key."); + + return cat_proxy(&_m_ess(), categ); + } + + /// Removes a category which is associated with the specified key + /** + * @param key The key of category to remove + */ + template + void assoc_erase(Key&& key) + { + using key_type = typename ::nana::detail::type_escape::type>::type; + + ::nana::key> wrap(key); + _m_erase_key(&wrap); + } + + bool assoc_ordered(bool); + + void auto_draw(bool); ///< Set state: Redraw automatically after an operation + template + void avoid_drawing(Function fn) + { + this->auto_draw(false); + try + { + fn(); + } + catch (...) + { + this->auto_draw(true); + throw; + } + this->auto_draw(true); + } + /// Scrolls the view to the first or last item of a specified category void scroll(bool to_bottom, size_type cat_pos = ::nana::npos); void scroll(bool to_bottom, const index_pair& pos); @@ -631,49 +716,32 @@ By \a clicking on one header the list get \a reordered, first up, and then down cat_proxy insert(cat_proxy, ::std::string); cat_proxy insert(cat_proxy, ::std::wstring); - cat_proxy at(size_type pos) const; - /// add categories in order when use a key? - listbox& ordered_categories(bool); + /// Inserts an item before a specified position + /** + * @param abs_pos The absolute position before which an item will be inserted. + * @param text Text of the first column, in UTF-8 encoded. + */ + void insert_item(const index_pair& abs_pos, ::std::string text); - /// return a proxy to tha cat with the key or create a new one in the right order - template - cat_proxy operator[](const Key & ck) - { - using catkey = typename ::nana::detail::type_escape::type; - std::shared_ptr p(new nana::key>(ck), [](nana::detail::key_interface* p) - { - delete p; - }); + /// Inserts an item before a specified position + /** + * @param abs_pos The absolute position before which an item will be inserted. + * @param text Text of the first column. + */ + void insert_item(const index_pair& abs_pos, ::std::wstring text); - return cat_proxy(&_m_ess(), _m_at_key(p)); - } - template - cat_proxy operator[](Key && ck) - { - using catkey = typename ::nana::detail::type_escape::type; - std::shared_ptr p(new nana::key>(std::move(ck)), [](nana::detail::key_interface* p) - { - delete p; - }); - - return cat_proxy(&_m_ess(), _m_at_key(p)); - } - - /// Returns an item by the specified absolute position - item_proxy at(const index_pair &abs_pos) const; /// Returns an index of item which contains the specified point. - index_pair at(const point & pos) const; + index_pair cast(const point & pos) const; + + /// add categories in order when use a key? + //listbox& ordered_categories(bool); //deprecated /// Returns the column which contains the specified point. columns_indexs column_from_pos(const point & pos); - - void insert(const index_pair&, ::std::string); /// - void erase_key(const Key& kv) + void erase_key(const Key& kv) //deprecated { typedef typename nana::detail::type_escape::type key_t; nana::key > key(kv); @@ -698,6 +767,7 @@ By \a clicking on one header the list get \a reordered, first up, and then down nana::key > key(std::move(kv)); _m_erase_key(&key); } + */ bool sortable() const; void sortable(bool enable); @@ -730,7 +800,7 @@ By \a clicking on one header the list get \a reordered, first up, and then down private: drawerbase::listbox::essence_t & _m_ess() const; nana::any* _m_anyobj(size_type cat, size_type index, bool allocate_if_empty) const; - drawerbase::listbox::category_t* _m_at_key(std::shared_ptr); + drawerbase::listbox::category_t* _m_assoc(std::shared_ptr, bool create_if_not_exists); void _m_erase_key(nana::detail::key_interface*); }; }//end namespace nana diff --git a/include/nana/key_type.hpp b/include/nana/key_type.hpp index ced29c50..9bca1e18 100644 --- a/include/nana/key_type.hpp +++ b/include/nana/key_type.hpp @@ -26,9 +26,9 @@ namespace nana }; //end class key_interface //Use less compare for equal compare [call it equal_by_less()?] - inline bool pred_equal_by_less(const key_interface * left, const key_interface* right) + inline bool pred_equal(const key_interface * left, const key_interface* right) { - return (left->compare(right) == false) && (right->compare(left) == false); + return (left->same_type(right) && (left->compare(right) == false) && (right->compare(left) == false)); } template diff --git a/source/gui/widgets/combox.cpp b/source/gui/widgets/combox.cpp index 374a9baa..6f5b6de3 100644 --- a/source/gui/widgets/combox.cpp +++ b/source/gui/widgets/combox.cpp @@ -364,7 +364,7 @@ namespace nana std::size_t pos = 0; for (auto & m : items_) { - if (m->key && detail::pred_equal_by_less(m->key.get(), p.get())) + if (m->key && detail::pred_equal(m->key.get(), p.get())) return pos; ++pos; } @@ -384,7 +384,7 @@ namespace nana std::size_t pos = 0; for (auto & m : items_) { - if (m->key && detail::pred_equal_by_less(m->key.get(), kv)) + if (m->key && detail::pred_equal(m->key.get(), kv)) { erase(pos); return; diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 41254770..8aca3d79 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -872,11 +872,20 @@ namespace nana { for (auto i = list_.begin(); i != list_.end(); ++i) { - if (i->key_ptr && i->key_ptr->compare(ptr.get())) + if (i->key_ptr) { - i = list_.emplace(i); - i->key_ptr = ptr; - return &(*i); + if (!i->key_ptr->same_type(ptr.get())) + { + this->ordered_categories_ = false; + break; + } + else if (ptr->compare(i->key_ptr.get())) + { + + i = list_.emplace(i); + i->key_ptr = ptr; + return &(*i); + } } } @@ -892,13 +901,13 @@ namespace nana } /// Insert before item in absolute "pos" a new item with "text" in column 0, and place it in last display position of this cat - bool insert(const index_pair& pos, std::string&& text) + void insert(const index_pair& pos, std::string&& text) { auto & catobj = *get(pos.cat); const auto n = catobj.items.size(); if (pos.item > n) - return false; + throw std::out_of_range("listbox: insert an item at invalid position"); catobj.sorted.push_back(n); @@ -906,8 +915,6 @@ namespace nana catobj.items.emplace(catobj.items.begin() + pos.item, std::move(text)); else catobj.items.emplace_back(std::move(text)); - - return true; } /// convert from display order to absolute (find the real item in that display pos) but without check from current active sorting, in fact using just the last sorting !!! @@ -1178,10 +1185,28 @@ namespace nana //Enable/Disable the ordered categories bool enable_ordered(bool enb) { - if (ordered_categories_ == enb) - return false; + if (ordered_categories_ != enb) + { + if (enb) + { + ::nana::detail::key_interface * refkey = nullptr; + for (auto i = list_.begin(); i != list_.end(); ++i) + { + if (i->key_ptr) + { + if (refkey) + { + if (!i->key_ptr->same_type(refkey)) + return false; + } + else + refkey = i->key_ptr.get(); + } + } + } - ordered_categories_ = enb; + ordered_categories_ = enb; + } return true; } @@ -4352,6 +4377,17 @@ namespace nana create(wd, r, visible); } + bool listbox::assoc_ordered(bool enable) + { + internal_scope_guard lock; + + auto & ess = _m_ess(); + if (ess.lister.enable_ordered(enable)) + ess.update(); + + return true; + } + void listbox::auto_draw(bool ad) { _m_ess().set_auto_draw(ad); @@ -4486,16 +4522,57 @@ namespace nana return cat_proxy{ &ess, new_cat_ptr }; } - listbox::cat_proxy listbox::at(size_type pos) const + + void listbox::insert_item(const index_pair& pos, std::string text) + { + internal_scope_guard lock; + auto & ess = _m_ess(); + ess.lister.insert(pos, std::move(text)); + + if (!empty()) + { + auto & item = ess.lister.at(pos); + item.bgcolor = bgcolor(); + item.fgcolor = fgcolor(); + ess.update(); + } + } + + void listbox::insert_item(const index_pair& pos, std::wstring text) + { + insert_item(pos, to_utf8(text)); + } + + listbox::cat_proxy listbox::at(size_type pos) + { + auto & ess = _m_ess(); + if (pos >= ess.lister.size_categ()) + throw std::out_of_range("Nana.Listbox.at(): invalid position"); + + return{ &ess, pos }; + } + + const listbox::cat_proxy listbox::at(size_type pos) const { auto & ess = _m_ess(); if(pos >= ess.lister.size_categ()) throw std::out_of_range("Nana.Listbox.at(): invalid position"); - return cat_proxy(&ess, pos); + return{ &ess, pos }; } - listbox& listbox::ordered_categories(bool enable_ordered) + listbox::item_proxy listbox::at(const index_pair& abs_pos) + { + return at(abs_pos.cat).at(abs_pos.item); + } + + const listbox::item_proxy listbox::at(const index_pair& pos_abs) const + { + return at(pos_abs.cat).at(pos_abs.item); + } + + /* + listbox& listbox::ordered_categories(bool enable_ordered) //deprecated { internal_scope_guard lock; @@ -4505,15 +4582,11 @@ namespace nana return *this; } - - listbox::item_proxy listbox::at(const index_pair& pos_abs) const - { - return at(pos_abs.cat).at(pos_abs.item); - } + */ // Contributed by leobackes(pr#97) - listbox::index_pair listbox::at ( const point& pos ) const + listbox::index_pair listbox::cast( const point& pos ) const { auto & ess=_m_ess(); auto _where=ess.where(pos.x, pos.y); @@ -4534,27 +4607,6 @@ namespace nana return col; } - void listbox::insert(const index_pair& pos, std::string text) - { - internal_scope_guard lock; - auto & ess = _m_ess(); - if (ess.lister.insert(pos, std::move(text))) - { - if (!empty()) - { - auto & item = ess.lister.at(pos); - item.bgcolor = bgcolor(); - item.fgcolor = fgcolor(); - ess.update(); - } - } - } - - void listbox::insert(const index_pair& pos, std::wstring text) - { - insert(pos, to_utf8(text)); - } - void listbox::checkable(bool chkable) { auto & ess = _m_ess(); @@ -4767,7 +4819,7 @@ namespace nana return _m_ess().lister.anyobj(index_pair{cat, index}, allocate_if_empty); } - drawerbase::listbox::category_t* listbox::_m_at_key(std::shared_ptr ptr) + drawerbase::listbox::category_t* listbox::_m_assoc(std::shared_ptr ptr, bool create_if_not_exists) { auto & ess = _m_ess(); @@ -4775,10 +4827,13 @@ namespace nana for (auto & m : ess.lister.cat_container()) { - if (m.key_ptr && nana::detail::pred_equal_by_less(ptr.get(), m.key_ptr.get())) + if (m.key_ptr && nana::detail::pred_equal(ptr.get(), m.key_ptr.get())) return &m; } + if (!create_if_not_exists) + return nullptr; + drawerbase::listbox::category_t* cat; if (ess.lister.enable_ordered()) @@ -4801,7 +4856,7 @@ namespace nana internal_scope_guard lock; for (auto i = cont.begin(); i != cont.end(); ++i) { - if (i->key_ptr && nana::detail::pred_equal_by_less(p, i->key_ptr.get())) + if (i->key_ptr && nana::detail::pred_equal(p, i->key_ptr.get())) { cont.erase(i); return;