nana/include/nana/gui/widgets/listbox.hpp
2015-04-27 14:19:09 +02:00

618 lines
19 KiB
C++

/**
* A List Box Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2015 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/gui/widgets/listbox.hpp
* @contributors: Ariel Vina-Rodriguez
*
*/
#ifndef NANA_GUI_WIDGETS_LISTBOX_HPP
#define NANA_GUI_WIDGETS_LISTBOX_HPP
#include "widget.hpp"
#include <nana/concepts.hpp>
#include <nana/key_type.hpp>
//#include <nana/paint/graphics.hpp>
#include <functional>
#include <initializer_list>
namespace nana
{
class listbox;
namespace drawerbase
{
namespace listbox
{
using size_type = std::size_t ;
struct cell
{
struct format
{
::nana::color bgcolor;
::nana::color fgcolor;
/// ::nana::paint::font font; \todo
format() = default;
format(const ::nana::color& bgcolor, const ::nana::color& fgcolor);
};
using format_ptr = std::unique_ptr < format > ;
::nana::string text;
format_ptr custom_format;
cell() = default;
cell(const cell&);
cell(cell&&);
cell(nana::string);
cell(nana::string, const format&);
cell(nana::string, const ::nana::color& bgcolor, const ::nana::color& fgcolor);
cell& operator=(const cell&);
cell& operator=(cell&&);
};
class oresolver
{
public:
oresolver& operator<<(bool);
oresolver& operator<<(short);
oresolver& operator<<(unsigned short);
oresolver& operator<<(int);
oresolver& operator<<(unsigned int);
oresolver& operator<<(long);
oresolver& operator<<(unsigned long);
oresolver& operator<<(long long);
oresolver& operator<<(unsigned long long);
oresolver& operator<<(float);
oresolver& operator<<(double);
oresolver& operator<<(long double);
oresolver& operator<<(const char*);
oresolver& operator<<(const wchar_t*);
oresolver& operator<<(const std::string&);
oresolver& operator<<(const std::wstring&);
oresolver& operator<<(std::wstring&&);
oresolver& operator<<(cell);
oresolver& operator<<(std::nullptr_t);
std::vector<cell> && move_cells();
private:
std::vector<cell> cells_;
};
class iresolver
{
public:
iresolver(const std::vector<cell>&);
iresolver& operator>>(bool&);
iresolver& operator>>(short&);
iresolver& operator>>(unsigned short&);
iresolver& operator>>(int&);
iresolver& operator>>(unsigned int&);
iresolver& operator>>(long&);
iresolver& operator>>(unsigned long&);
iresolver& operator>>(long long&);
iresolver& operator>>(unsigned long long&);
iresolver& operator>>(float&);
iresolver& operator>>(double&);
iresolver& operator>>(long double&);
iresolver& operator>>(std::string&);
iresolver& operator>>(std::wstring&);
iresolver& operator>>(cell&);
iresolver& operator>>(std::nullptr_t);
private:
const std::vector<cell>& cells_;
std::size_t pos_{0};
};
/// usefull for both absolute and display (sorted) positions
struct index_pair
{
size_type cat; //The pos of category
size_type item; //the pos of item in a category.
index_pair(size_type cat_pos = 0, size_type item_pos = 0)
: cat(cat_pos),
item(item_pos)
{}
bool empty() const
{
return (npos == cat);
}
void set_both(size_type n)
{
cat = item = n;
}
bool is_category() const
{
return (npos != cat && npos == item);
}
bool is_item() const
{
return (npos != cat && npos != item);
}
bool operator==(const index_pair& r) const
{
return (r.cat == cat && r.item == item);
}
bool operator!=(const index_pair& r) const
{
return !this->operator==(r);
}
bool operator>(const index_pair& r) const
{
return (cat > r.cat) || (cat == r.cat && item > r.item);
}
};
typedef std::vector<index_pair> selection;
//struct essence_t
//@brief: this struct gives many data for listbox,
// the state of the struct does not effect on member funcions, therefore all data members are public.
struct essence_t;
struct category_t;
class drawer_header_impl;
class drawer_lister_impl;
/// mostly works on display positions
class trigger: public drawer_trigger
{
public:
trigger();
~trigger();
essence_t& essence();
essence_t& essence() const;
void draw();
private:
void _m_draw_border();
private:
void attached(widget_reference, graph_reference) override;
void detached() override;
void typeface_changed(graph_reference) override;
void refresh(graph_reference) override;
void mouse_move(graph_reference, const arg_mouse&) override;
void mouse_leave(graph_reference, const arg_mouse&) override;
void mouse_down(graph_reference, const arg_mouse&) override;
void mouse_up(graph_reference, const arg_mouse&) override;
void mouse_wheel(graph_reference, const arg_wheel&) override;
void dbl_click(graph_reference, const arg_mouse&) override;
void resized(graph_reference, const arg_resized&) override;
void key_press(graph_reference, const arg_keyboard&) override;
void key_char(graph_reference, const arg_keyboard&) override;
private:
essence_t * essence_;
drawer_header_impl *drawer_header_;
drawer_lister_impl *drawer_lister_;
};//end class trigger
/// operate with absolute positions and contain only the position but montain pointers to parts of the real items
/// item_proxy self, it references and iterators are not invalidated by sort()
class item_proxy
: public std::iterator<std::input_iterator_tag, item_proxy>
{
public:
item_proxy(essence_t*);
item_proxy(essence_t*, const index_pair&);
/// the main porpose of this it to make obvious that item_proxy operate with absolute positions, and dont get moved during sort()
static item_proxy from_display(essence_t *ess, const index_pair &relative) ;
item_proxy from_display(const index_pair &relative) const;
/// posible use: last_selected_display = last_selected.to_display().item; use with caution, it get invalidated after a sort()
index_pair to_display() const;
bool empty() const;
item_proxy & check(bool ck);
bool checked() const;
item_proxy & select(bool);
bool selected() const;
item_proxy & bgcolor(const nana::color&);
nana::color bgcolor() const;
item_proxy& fgcolor(const nana::color&);
nana::color fgcolor() const;
index_pair pos() const;
size_type columns() const;
item_proxy& text(size_type col, cell);
item_proxy& text(size_type col, nana::string);
nana::string text(size_type col) const;
void icon(const nana::paint::image&);
template<typename T>
item_proxy & resolve_from(const T& t)
{
oresolver ores;
ores << t;
auto && cells = ores.move_cells();
auto cols = columns();
cells.resize(cols);
for (auto pos = 0; pos < cols; ++pos)
{
auto & el = cells[pos];
if (el.text.size() == 1 && el.text[0] == nana::char_t(0))
continue;
text(pos, std::move(el));
}
return *this;
}
template<typename T>
void resolve_to(T& t) const
{
iresolver ires(_m_cells());
ires >> t;
}
template<typename T>
T* value_ptr() const
{
auto * pany = _m_value();
return (pany ? pany->get<T>() : nullptr);
}
template<typename T>
T & value() const
{
auto * pany = _m_value();
if(nullptr == pany)
throw std::runtime_error("listbox::item_proxy.value<T>() is empty");
T * p = pany->get<T>();
if(nullptr == p)
throw std::runtime_error("listbox::item_proxy.value<T>() invalid type of value");
return *p;
}
template<typename T>
item_proxy & value(T&& t)
{
*_m_value(true) = std::forward<T>(t);
return *this;
}
/// Behavior of Iterator's value_type
bool operator==(const nana::string& s) const;
bool operator==(const char * s) const;
bool operator==(const wchar_t * s) const;
/// Behavior of Iterator
item_proxy & operator=(const item_proxy&);
/// Behavior of Iterator
item_proxy & operator++();
/// Behavior of Iterator
item_proxy operator++(int);
/// Behavior of Iterator
item_proxy& operator*();
/// Behavior of Iterator
const item_proxy& operator*() const;
/// Behavior of Iterator
item_proxy* operator->();
/// Behavior of Iterator
const item_proxy* operator->() const;
/// Behavior of Iterator
bool operator==(const item_proxy&) const;
/// Behavior of Iterator
bool operator!=(const item_proxy&) const;
//Undocumented method
essence_t * _m_ess() const;
private:
std::vector<cell> & _m_cells() const;
nana::any * _m_value(bool alloc_if_empty);
const nana::any * _m_value() const;
private:
essence_t * ess_;
category_t* cat_{nullptr};
index_pair pos_;
};
class cat_proxy
: public std::iterator < std::input_iterator_tag, cat_proxy >
{
public:
cat_proxy() = default;
cat_proxy(essence_t*, size_type pos);
cat_proxy(essence_t*, category_t*);
/// Append an item at abs end of the category, set_value determines whether assign T object to the value of item.
template<typename T>
item_proxy append(T&& t, bool set_value = false)
{
oresolver ores;
if (set_value)
ores << t; //copy it if it is rvalue and set_value is true.
else
ores << std::forward<T>(t);
_m_append(ores.move_cells());
item_proxy iter{ ess_, index_pair(pos_, size() - 1) };
if (set_value)
iter.value(std::forward<T>(t));
return iter;
}
void append(std::initializer_list<nana::string>);
size_type columns() const;
cat_proxy& text(nana::string);
nana::string text() const;
/// Behavior of a container
void push_back(nana::string);
item_proxy begin() const;
item_proxy end() const;
item_proxy cbegin() const;
item_proxy cend() const;
item_proxy at(size_type pos_abs) const;
item_proxy back() const;
/// Returns the absolute index of a item by its display pos, the index of the item isn't changed after sorting.
/// 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 !!!
size_type index_by_display_order(size_type disp_order) const;
/// find display order for the real item but without check from current active sorting, in fact using just the last sorting !!!
size_type display_order(size_type pos) const;
/// this cat position
size_type position() const;
/// Returns the number of items
size_type size() const;
/// Behavior of Iterator
cat_proxy& operator=(const cat_proxy&);
/// Behavior of Iterator
cat_proxy & operator++();
/// Behavior of Iterator
cat_proxy operator++(int);
/// Behavior of Iterator
cat_proxy& operator*();
/// Behavior of Iterator
const cat_proxy& operator*() const;
/// Behavior of Iterator
cat_proxy* operator->();
/// Behavior of Iterator
const cat_proxy* operator->() const;
/// Behavior of Iterator
bool operator==(const cat_proxy&) const;
/// Behavior of Iterator
bool operator!=(const cat_proxy&) const;
private:
void _m_append(std::vector<cell> && cells);
void _m_cat_by_pos();
private:
essence_t* ess_{nullptr};
category_t* cat_{nullptr};
size_type pos_{0}; ///< Absolute position, not relative to display, and dont change during sort()
};
struct export_options
{
nana::string sep = nana::string {STR("\t" )},
endl= nana::string {STR("\n")} ;
bool only_selected_items{true},
only_checked_items {false},
only_visible_columns{true};
using columns_indexs = std::vector<size_type>;
columns_indexs columns_order;
};
}
}//end namespace drawerbase
struct arg_listbox
: public event_arg
{
mutable drawerbase::listbox::item_proxy item;
bool selected;
arg_listbox(const drawerbase::listbox::item_proxy&, bool selected);
};
namespace drawerbase
{
namespace listbox
{
struct listbox_events
: public general_events
{
basic_event<arg_listbox> checked;
basic_event<arg_listbox> selected;
};
struct scheme
: public widget_colors
{
color_proxy header_bgcolor{static_cast<color_rgb>(0xf1f2f4)};
color_proxy header_grabbed{ static_cast<color_rgb>(0x8BD6F6)};
color_proxy header_floated{ static_cast<color_rgb>(0xBABBBC)};
color_proxy item_selected{ static_cast<color_rgb>(0xD5EFFC) };
};
}
}//end namespace drawerbase
/*! \brief A rectangle containing a list of strings from which the user can select. This widget contain a list of \a categories, with in turn contain a list of \a items.
A category is a text with can be \a selected, \a checked and \a expanded to show the items.
An item is formed by \a column-fields, each corresponding to one of the \a headers.
An item can be \a selected and \a checked.
The user can \a drag the header to \a reisize it or to \a reorganize it.
By \a clicking on a header the list get \a reordered, first up, and then down alternatively.
*/
class listbox
: public widget_object<category::widget_tag, drawerbase::listbox::trigger, drawerbase::listbox::listbox_events, drawerbase::listbox::scheme>,
public concepts::any_objective<drawerbase::listbox::size_type, 2>
{
public:
using size_type = drawerbase::listbox::size_type;
using index_pair = drawerbase::listbox::index_pair;
using cat_proxy = drawerbase::listbox::cat_proxy;
using item_proxy = drawerbase::listbox::item_proxy;
using selection = drawerbase::listbox::selection; ///<A container type for items.
using iresolver = drawerbase::listbox::iresolver;
using oresolver = drawerbase::listbox::oresolver;
using cell = drawerbase::listbox::cell;
using export_options= drawerbase::listbox::export_options;
using columns_indexs= drawerbase::listbox::size_type;
public:
listbox() = default;
listbox(window, bool visible);
listbox(window, const rectangle& = {}, bool visible = true);
void auto_draw(bool); ///<Set state: Redraw automatically after an operation?
void append_header(nana::string, unsigned width = 120);///<Appends a new column with a header text and the specified width at the end
listbox& header_width(size_type pos, unsigned pixels);
unsigned header_width(size_type pos) const;
cat_proxy append(nana::string); ///<Appends a new category at the end
void append(std::initializer_list<nana::string>); ///<Appends categories at the end
cat_proxy insert(cat_proxy, nana::string);
cat_proxy at(size_type pos) const;
/// add categories in order when use a key?
listbox& ordered_categories(bool);
/// return a proxy to tha cat with the key or create a new one in the right order
template<typename Key>
cat_proxy operator[](const Key & ck)
{
using catkey = typename ::nana::detail::type_escape<Key>::type;
std::shared_ptr<nana::detail::key_interface> p(new nana::key<catkey, std::less<catkey>>(ck), [](nana::detail::key_interface* p)
{
delete p;
});
return cat_proxy(&_m_ess(), _m_at_key(p));
}
template<typename Key>
cat_proxy operator[](Key && ck)
{
using catkey = typename ::nana::detail::type_escape<Key>::type;
std::shared_ptr<nana::detail::key_interface> p(new nana::key<catkey, std::less<catkey>>(std::move(ck)), [](nana::detail::key_interface* p)
{
delete p;
});
return cat_proxy(&_m_ess(), _m_at_key(p));
}
item_proxy at(const index_pair &abs_pos) const;
void insert(const index_pair&, nana::string); ///<Insert a new item with a text in the first column.
void checkable(bool);
selection checked() const; ///<Returns the items which are checked.
void clear(size_type cat); ///<Removes all the items from the specified category
void clear(); ///<Removes all the items from all categories
void erase(size_type cat); ///<Erases a category
void erase(); ///<Erases all categories.
item_proxy erase(item_proxy);
template<typename Key>
void erase_key(const Key& kv)
{
typedef typename nana::detail::type_escape<Key>::type key_t;
nana::key<key_t, std::less<key_t> > key(kv);
_m_ease_key(&key);
}
template<typename Key>
void erase_key(Key&& kv)
{
typedef typename nana::detail::type_escape<Key>::type key_t;
nana::key<key_t, std::less<key_t> > key(std::move(kv));
_m_ease_key(&key);
}
///Sets a strict weak ordering comparer for a column
void set_sort_compare(size_type col, std::function<bool(const nana::string&, nana::any*,
const nana::string&, nana::any*, bool reverse)> strick_ordering);
/// sort() and ivalidate any existing reference from display position to absolute item, that is: after sort() display offset point to different items
void sort_col(size_type col, bool reverse = false);
size_type sort_col() const;
/// potencially ivalidate any existing reference from display position to absolute item, that is: after sort() display offset point to different items
void unsort();
bool freeze_sort(bool freeze);
selection selected() const; ///<Get the absolute indexs of all the selected items
void show_header(bool);
bool visible_header() const;
void move_select(bool upwards); ///<Selects an item besides the current selected item in the display.
size_type size_categ() const; ///<Get the number of categories
size_type size_item() const; ///<The number of items in the default category
size_type size_item(size_type cat) const; ///<The number of items in category "cat"
void enable_single(bool for_selection, bool category_limited);
void disable_single(bool for_selection);
export_options& def_export_options();
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<nana::detail::key_interface>);
void _m_ease_key(nana::detail::key_interface*);
};
namespace dev
{
template<>
struct widget_traits<listbox>
{
using event_type = drawerbase::listbox::listbox_events;
using scheme_type = drawerbase::listbox::scheme;
};
}
}//end namespace nana
#endif