Merge branch 'toborrobot-develop' into hotfix-1.4.1

This commit is contained in:
Jinhao 2016-11-14 05:34:45 +08:00
commit 8594d175e1
10 changed files with 794 additions and 46 deletions

View File

@ -113,6 +113,7 @@ namespace nana
window window_handle() const;
void div(const char* s); ///< Divides the attached widget into fields.
const std::string& div() const noexcept; ///< Returns div-text that depends on fields status.
void modify(const char* field_name, const char* div_text); ///< Modifies a specified field.
field_reference field(const char* name);///< Returns a field with the specified name.

View File

@ -8,12 +8,7 @@
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/widgets/listbox.hpp
* @contributors:
* Hiroshi Seki
* Ariel Vina-Rodriguez
* leobackes(pr#86,pr#97)
* Benjamin Navarro(pr#81)
* besh81(pr#130)
*
*/
#ifndef NANA_GUI_WIDGETS_LISTBOX_HPP
@ -800,12 +795,31 @@ namespace nana
/// posible use: last_selected_display = last_selected.to_display().item; use with caution, it get invalidated after a sort()
index_pair to_display() const;
/// Determines whether the item is displayed on the screen
bool displayed() const;
bool empty() const;
item_proxy & check(bool ck);
/// Checks/unchecks the item
/**
* @param chk Indicates whether to check or uncheck the item
* @param scroll_view Indicates whether to scroll the view to the item. It is ignored if the item is displayed.
* @return the reference of *this.
*/
item_proxy & check(bool chk, bool scroll_view = false);
/// Determines whether the item is checked
bool checked() const;
item_proxy & select(bool);
/// Selects/unselects the item
/**
* @param sel Indicates whether to select or unselect the item
* @param scroll_view Indicates whether to scroll the view to the item. It is ignored if the item is displayed.
* @return the reference of *this.
*/
item_proxy & select(bool sel, bool scroll_view = false);
/// Determines whether he item is selected
bool selected() const;
item_proxy & bgcolor(const nana::color&);
@ -1130,9 +1144,11 @@ namespace nana
: public widget_geometrics
{
color_proxy header_bgcolor{static_cast<color_rgb>(0xf1f2f4)};
color_proxy header_fgcolor{ colors::black };
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) };
color_proxy item_highlighted{ static_cast<color_rgb>(0xD5EFFC) };
/// The max column width which is generated by fit_content is allowed. It is ignored when it is 0, or a max value is passed to fit_content.
unsigned max_fit_content{ 0 };
@ -1435,7 +1451,7 @@ the nana::detail::basic_window member pointer scheme
export_options& def_export_options();
private:
drawerbase::listbox::essence & _m_ess() const;
nana::any* _m_anyobj(size_type cat, size_type index, bool allocate_if_empty) const;
nana::any* _m_anyobj(size_type cat, size_type index, bool allocate_if_empty) const override;
drawerbase::listbox::category_t* _m_assoc(std::shared_ptr<nana::detail::key_interface>, bool create_if_not_exists);
void _m_erase_key(nana::detail::key_interface*);
};

View File

@ -1,13 +1,14 @@
/**
* A Menu implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2009-2014 Jinhao(cnjinhao@hotmail.com)
* Copyright(C) 2009-2016 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/menu.hpp
*
*/
#ifndef NANA_GUI_WIDGETS_MENU_HPP
@ -122,12 +123,13 @@ namespace nana
~menu();
/// Appends an item to the menu.
item_proxy append(const std::string& text, const event_fn_t& callback= event_fn_t());
item_proxy append(std::string text_utf8, const event_fn_t& callback= event_fn_t());
void append_splitter();
void clear(); ///< Erases all of the items.
/// Closes the menu. It does not destroy the menu; just close the window for the menu.
void close();
void image(std::size_t pos, const paint::image& icon);
void text(std::size_t pos, std::string text_utf8);
void check_style(std::size_t pos, checks);
void checked(std::size_t pos, bool);
bool checked(std::size_t pos) const;

View File

@ -16,6 +16,7 @@
#include <sstream>
#include <functional>
#include <memory>
#include <nana/deploy.hpp>
namespace nana
{

View File

@ -201,7 +201,7 @@ namespace nana
#if defined(NANA_WINDOWS)
void notifications_window_proc(HWND wd, WPARAM wparam, LPARAM lparam)
{
arg_notifier arg;
arg_notifier arg = {};
switch (lparam)
{
case WM_LBUTTONDBLCLK:
@ -321,6 +321,7 @@ namespace nana
void notifier::icon(const std::string& icon_file)
{
#if defined(NANA_WINDOWS)
paint::image image_ico{ icon_file };
auto icon_handle = paint::image_accessor::icon(image_ico);
if (icon_handle)
@ -330,14 +331,17 @@ namespace nana
impl_->set_icon(image_ico);
impl_->icon = image_ico;
}
#endif
}
void notifier::insert_icon(const std::string& icon_file)
{
#if defined(NANA_WINDOWS)
paint::image image_ico{ icon_file };
auto icon_handle = paint::image_accessor::icon(image_ico);
if (icon_handle)
impl_->icons.emplace_back(static_cast<paint::image&&>(image_ico));
#endif
}
void notifier::period(unsigned ms)

View File

@ -9,6 +9,7 @@
*
* @file: nana/gui/place.cpp
* @contributors: Ariel Vina-Rodriguez
* dankan1890(PR#156)
*/
#include <cfloat>
@ -56,7 +57,7 @@ namespace nana
{
div_start, div_end, splitter,
identifier, dock, vert, grid, number, array, reparray,
weight, gap, margin, arrange, variable, repeated, min_px, max_px, left, right, top, bottom,
weight, gap, margin, arrange, variable, repeated, min_px, max_px, left, right, top, bottom, undisplayed, invisible,
collapse, parameters,
equal,
eof, error
@ -275,7 +276,7 @@ namespace nana
_m_throw_error("a parameter list is required after 'collapse'");
return token::collapse;
}
else if ("left" == idstr_ || "right" == idstr_ || "top" == idstr_ || "bottom" == idstr_)
else if ("left" == idstr_ || "right" == idstr_ || "top" == idstr_ || "bottom" == idstr_ || "undisplayed" == idstr_ || "invisible" == idstr_)
{
switch (idstr_.front())
{
@ -283,8 +284,11 @@ namespace nana
case 'r': return token::right;
case 't': return token::top;
case 'b': return token::bottom;
case 'u': return token::undisplayed;
case 'i': return token::invisible;
}
}
return token::identifier;
}
@ -407,8 +411,18 @@ namespace nana
sp = _m_eat_whitespace(sp);
if ('%' == *sp)
{
if (number_t::kind::integer == number_.kind_of())
switch (number_.kind_of())
{
case number_t::kind::integer:
number_.assign_percent(number_.integer());
break;
case number_t::kind::real:
number_.assign_percent(number_.real());
break;
default:
break;
}
return sp - allstart + 1;
}
return sp - allstart;
@ -427,6 +441,87 @@ namespace nana
}; //end class tokenizer
}
inline bool is_idchar(int ch)
{
return ('_' == ch || isalnum(ch));
}
std::size_t find_idstr(const std::string& text, const char* idstr, std::size_t off = 0)
{
const auto len = std::strlen(idstr);
size_t pos;
while ((pos = text.find(idstr, off)) != text.npos)
{
if (!is_idchar(text[pos + len]))
{
if (pos == 0 || !is_idchar(text[pos - 1]))
return pos;
}
off = pos + len; // occurrence not found, advancing the offset and try again
}
return text.npos;
}
std::pair<std::size_t, std::size_t> get_field_bound(const std::string& div, const char* idstr, int depth)
{
auto start_pos = find_idstr(div, idstr);
if (depth < 0 || start_pos >= div.length())
return{};
const char* p = div.c_str() + start_pos;
while (depth >= 0)
{
auto pos = div.find_last_of("<>", start_pos);
if (div.npos == pos)
return{};
if (div[pos] == '>')
{
++depth;
start_pos = pos - 1;
continue;
}
if (0 == depth)
{
start_pos = pos;
break;
}
--depth;
start_pos = pos - 1;
}
auto off = start_pos + 1;
while (true)
{
auto pos = div.find_first_of("<>", off);
if (div.npos == pos)
return{};
if ('<' == div[pos])
{
++depth;
off = pos + 1;
continue;
}
if (0 == depth)
return{ start_pos, pos + 1 };
--depth;
off = pos + 1;
}
}
//struct implement
struct place::implement
{
@ -442,6 +537,8 @@ namespace nana
window window_handle{nullptr};
event_handle event_size_handle{nullptr};
std::string div_text;
std::unique_ptr<division> root_division;
std::map<std::string, field_gather*> fields;
std::map<std::string, field_dock*> docks;
@ -1309,8 +1406,9 @@ namespace nana
enum{splitter_px = 4};
public:
div_splitter(place_parts::number_t init_weight)
: division(kind::splitter, std::string()),
div_splitter(place_parts::number_t init_weight, implement* impl):
division(kind::splitter, std::string()),
impl_(impl),
init_weight_(init_weight)
{
this->weight.assign(splitter_px);
@ -1335,7 +1433,7 @@ namespace nana
auto grab_fn = [this](const arg_mouse& arg)
{
if (false == arg.left_button)
if ((false == arg.left_button) && (mouse::left_button != arg.button))
return;
if (event_code::mouse_down == arg.evt_code)
@ -1368,6 +1466,7 @@ namespace nana
else if(event_code::mouse_up == arg.evt_code)
{
grabbed_ = false;
this->_m_update_div(impl_->div_text);
}
else if (event_code::mouse_move == arg.evt_code)
{
@ -1466,6 +1565,231 @@ namespace nana
splitter_.move(this->field_area);
}
private:
static int _m_search_name(const division* div, std::string& name)
{
if (div->name.size())
{
name = div->name;
return 0;
}
for (auto & child : div->children)
{
if (child->name.size())
{
name = child->name;
return 1;
}
}
for (auto & child : div->children)
{
auto depth = _m_search_name(child.get(), name);
if (depth >= 0)
return depth + 1;
}
return -1;
}
static std::pair<std::size_t, std::size_t> _m_field_bound(const std::string& div, std::size_t start_pos)
{
int depth = 0;
if ('<' == div[start_pos])
{
auto off = start_pos + 1;
while (off < div.length())
{
auto pos = div.find_first_of("<>", off);
if (div.npos == pos)
break;
if ('<' == div[pos])
{
++depth;
off = pos + 1;
continue;
}
if (0 == depth)
return{ start_pos, pos + 1};
--depth;
off = pos + 1;
}
}
else if (('>' == div[start_pos]) && (start_pos > 0))
{
auto off = start_pos - 1;
while (true)
{
auto pos = div.find_last_of("<>", off);
if (div.npos == pos)
break;
if ('>' == div[pos])
{
++depth;
if (0 == pos)
break;
off = pos - 1;
}
if (0 == depth)
return{ pos, start_pos + 1};
if (0 == pos)
break;
off = pos - 1;
}
}
return{};
}
static void _m_remove_attr(std::string& div, const char* attr)
{
auto attr_pos = div.find(attr);
if (div.npos == attr_pos)
return;
std::size_t off = 1;
while (true)
{
auto pos = div.find('<', off);
if (div.npos == pos)
break;
if (attr_pos < pos)
break;
int depth = 0;
off = pos + 1;
std::size_t endpos = 0;
while (true)
{
endpos = div.find_first_of("<>", off);
if (div.npos == endpos)
return;
if ('<' == div[endpos])
{
++depth;
off = endpos + 1;
continue;
}
if (0 == depth)
break;
--depth;
off = endpos + 1;
}
if (attr_pos < endpos)
{
attr_pos = div.find(attr, endpos + 1);
if (div.npos == attr_pos)
return;
}
off = endpos + 1;
}
auto len = std::strlen(attr);
auto endpos = div.find_first_not_of(" ", attr_pos + len);
if (div.npos != endpos && div[endpos] == '=')
{
endpos = div.find_first_not_of(" 0123456789.%", endpos + 1);
if (div.npos == endpos)
throw std::runtime_error("please report an issue if throws");
}
else
endpos = attr_pos + len;
div.erase(attr_pos, endpos - attr_pos);
}
void _m_update_div(std::string& div)
{
std::string name;
bool left = true;
//Search a name recursively from a specified leaf field.
//It returns the depth from the leaf div to the div which has a name.
auto depth = _m_search_name(_m_leaf_left(), name);
if (-1 == depth)
{
left = false;
depth = _m_search_name(_m_leaf_right(), name);
if (-1 == depth)
return;
}
//Get the bound of field div-text through reverse recursion.
auto bound = get_field_bound(div, name.c_str(), depth);
if (bound.first == bound.second)
return;
auto fieldstr = div.substr(bound.first, bound.second - bound.first);
_m_remove_attr(fieldstr, "weight");
decltype(bound) other_bound;
if (left)
{
//Get the bound of leaf right
auto pos = div.find('<', bound.second + 1);
if (div.npos == pos)
throw std::runtime_error("please report an issue if it throws");
other_bound = _m_field_bound(div, pos);
}
else
{
//Get the bound of leaf left
auto pos = div.rfind('>', bound.first - 1);
if (div.npos == pos)
throw std::runtime_error("place report an issue if it throws");
other_bound = _m_field_bound(div, pos);
}
auto other_fieldstr = div.substr(other_bound.first, other_bound.second - other_bound.first);
_m_remove_attr(other_fieldstr, "weight");
const bool vert = (::nana::cursor::size_we != splitter_cursor_);
rectangle_rotator r_left(vert, _m_leaf_left()->field_area);
rectangle_rotator r_right(vert, _m_leaf_right()->field_area);
rectangle_rotator r_owner(vert, this->div_owner->field_area);
double percent = double((left ? r_left : r_right).w()) / double(r_owner.w());
std::string weight = "weight=" + std::to_string(percent * 100) + "% ";
fieldstr.insert(1, weight);
//Replaces the 'right' field before 'left' in order to make the bound consistent
if (left)
{
if (other_fieldstr.length() != (other_bound.second - other_bound.first))
div.replace(other_bound.first, other_bound.second - other_bound.first, other_fieldstr);
div.replace(bound.first, bound.second - bound.first, fieldstr);
}
else
{
div.replace(bound.first, bound.second - bound.first, fieldstr);
if (other_fieldstr.length() != (other_bound.second - other_bound.first))
div.replace(other_bound.first, other_bound.second - other_bound.first, other_fieldstr);
}
}
division * _m_leaf_left() const
{
return previous();
@ -1526,6 +1850,7 @@ namespace nana
return area;
}
private:
implement* const impl_;
nana::cursor splitter_cursor_{nana::cursor::arrow};
place_parts::splitter<true> splitter_;
nana::point begin_point_;
@ -1885,7 +2210,7 @@ namespace nana
unsigned vert_count = 0, horz_count = 0;
bool is_first = true;
bool prev_attr;
bool prev_attr = false;
for (auto & child : children)
{
@ -1962,7 +2287,7 @@ namespace nana
child_dv->splitter.reset();
::nana::rectangle child_r;
double split_range_begin = -1, split_range_end;
double split_range_begin = -1, split_range_end = 0;
switch (child->dir)
{
default:
@ -2147,6 +2472,9 @@ namespace nana
std::vector<std::unique_ptr<division>> children;
::nana::direction div_dir = ::nana::direction::west;
bool undisplayed = false;
bool invisible = false;
for (token tk = tknizer.read(); tk != token::eof; tk = tknizer.read())
{
bool exit_for = false;
@ -2162,7 +2490,7 @@ namespace nana
//Ignore the splitter when there is not a division.
if (!children.empty() && (division::kind::splitter != children.back()->kind_of_division))
{
auto splitter = new div_splitter(tknizer.number());
auto splitter = new div_splitter(tknizer.number(), this);
children.back()->div_next = splitter;
children.emplace_back(std::unique_ptr<division>{ splitter });
}
@ -2305,6 +2633,10 @@ namespace nana
div_dir = ::nana::direction::north; break;
case token::bottom:
div_dir = ::nana::direction::south; break;
case token::undisplayed:
undisplayed = true; break;
case token::invisible:
invisible = true; break;
default: break;
}
if (exit_for)
@ -2430,6 +2762,9 @@ namespace nana
div->children.swap(children);
div->margin = std::move(margin);
div->dir = div_dir;
div->display = !undisplayed;
div->visible = !(undisplayed || invisible);
return div;
}
@ -2593,6 +2928,7 @@ namespace nana
impl_->connect(div.get());
impl_->root_division.reset(); //clear atachments div-fields
impl_->root_division.swap(div);
impl_->div_text.assign(s);
}
catch (...)
{
@ -2601,6 +2937,16 @@ namespace nana
}
}
const std::string& place::div() const noexcept
{
return impl_->div_text;
}
//Contributed by dankan1890(PR#156)
enum class update_operation { erase = 0, insert, replace };
void update_div(std::string& div, const char* field, const char* attr, update_operation operation);
void place::modify(const char* name, const char* div_text)
{
if (nullptr == div_text)
@ -2655,6 +3001,7 @@ namespace nana
impl_->check_unique(impl_->root_division.get());
impl_->connect(impl_->root_division.get());
impl_->tmp_replaced.reset();
update_div(impl_->div_text, name, div_text, update_operation::replace);
modified_ptr->div_owner = div_owner;
modified_ptr->div_next = div_next;
@ -2698,6 +3045,71 @@ namespace nana
return *p;
}
void update_div(std::string& div, const char* field, const char* attr, update_operation operation)
{
const auto fieldname_pos = find_idstr(div, field);
if (div.npos == fieldname_pos)
return;
auto bound = get_field_bound(div, field, 0);
auto fieldstr = div.substr(bound.first + 1, bound.second - bound.first - 2);
//Search the attribute
std::size_t pos = 0;
int depth = 0;
for (; true; ++pos)
{
pos = find_idstr(fieldstr, attr, pos);
if (fieldstr.npos == pos)
break;
//Check if the attr is belong to this field.
depth = 0;
std::size_t off = pos;
while (true)
{
off = fieldstr.find_last_of("<>", off);
if (fieldstr.npos == off)
break;
if ('>' == fieldstr[off])
++depth;
else
--depth;
if (0 == off)
break;
--off;
}
if (0 == depth)
break;
}
if (fieldstr.npos == pos)
{
//There is not an attribute
if (operation == update_operation::insert)
div.insert(fieldname_pos + std::strlen(field), " " + std::string(attr));
else if (operation == update_operation::replace)
{
div.erase(bound.first + 1, fieldstr.length());
div.insert(bound.first + 1, std::string(attr) + " " + std::string(field));
}
}
else
{
//There is an attribute
if (operation == update_operation::erase)
{
div.erase(bound.first + pos + 1, std::strlen(attr));
if ((div[bound.first + pos] == div[bound.first + pos + 1]) && (' ' == div[bound.first + pos]))
div.erase(bound.first + pos, 1);
}
}
}
void place::field_visible(const char* name, bool vsb)
{
if (!name) name = "";
@ -2707,7 +3119,10 @@ namespace nana
auto div = impl_->search_div_name(impl_->root_division.get(), name);
if (div)
{
div->set_visible(vsb);
update_div(impl_->div_text, name, "invisible", !vsb ? update_operation::insert : update_operation::erase);
}
}
bool place::field_visible(const char* name) const
@ -2730,8 +3145,12 @@ namespace nana
auto div = impl_->search_div_name(impl_->root_division.get(), name);
if (div)
{
update_div(impl_->div_text, name, "invisible", update_operation::erase);
update_div(impl_->div_text, name, "undisplayed", !dsp ? update_operation::insert : update_operation::erase);
div->set_display(dsp);
}
}
bool place::field_display(const char* name) const
{

View File

@ -14,6 +14,7 @@
* leobackes(pr#86,pr#97)
* Benjamin Navarro(pr#81)
* besh81(pr#130)
* dankan1890(pr#158)
*
*/
@ -3393,7 +3394,8 @@ namespace nana
const auto border_color = essence_->scheme_ptr->header_bgcolor.get_color().blend(colors::black, 0.8);
int text_top = (r.height - essence_->scheme_ptr->text_height) / 2 + r.y;
auto text_color = essence_->lister.wd_ptr()->fgcolor();
auto text_color = essence_->scheme_ptr->header_fgcolor.get_color();
auto state = item_state::normal;
//check whether grabing an item, if item_spliter_ != npos, that indicates the grab item is a spliter.
@ -3784,7 +3786,7 @@ namespace nana
if (item.flags.selected)
bgcolor = bgcolor.blend(colors::black, 0.98); // or "selected"
else
bgcolor = bgcolor.blend(essence_->scheme_ptr->item_selected, 0.7); /// \todo create a parametre for amount of blend
bgcolor = bgcolor.blend(essence_->scheme_ptr->item_highlighted, 0.7); /// \todo create a parametre for amount of blend
}
unsigned show_w = (std::min)(content_r.width, width - essence_->scroll.x_offset());
@ -4506,12 +4508,28 @@ namespace nana
return ess_->lister.relative_pair(pos_);
}
bool item_proxy::displayed() const
{
if (!ess_->lister.get(pos_.cat)->expand)
return false;
auto pos = to_display();
if (ess_->scroll.offset_y_dpl > pos)
return false;
auto size = ess_->number_of_lister_items(false);
auto last = ess_->lister.advance(ess_->scroll.offset_y_dpl, size);
return (last > pos || last == pos);
}
bool item_proxy::empty() const
{
return !ess_;
}
item_proxy & item_proxy::check(bool ck)
item_proxy & item_proxy::check(bool ck, bool scroll_view)
{
internal_scope_guard lock;
auto & m = cat_->items.at(pos_.item);
@ -4520,6 +4538,15 @@ namespace nana
m.flags.checked = ck;
ess_->lister.emit_checked(pos_);
if (scroll_view)
{
if (ess_->lister.get(pos_.cat)->expand)
ess_->lister.get(pos_.cat)->expand = false;
if (!this->displayed())
ess_->lister.scroll(pos_, !(ess_->scroll.offset_y_dpl > this->to_display()));
}
ess_->update();
}
return *this;
@ -4531,14 +4558,15 @@ namespace nana
}
/// is ignored if no change (maybe set last_selected anyway??), but if change emit event, deselect others if need ans set/unset last_selected
item_proxy & item_proxy::select(bool s)
item_proxy & item_proxy::select(bool sel, bool scroll_view)
{
internal_scope_guard lock;
//pos_ never represents a category if this item_proxy is available.
auto & m = cat_->items.at(pos_.item); // a ref to the real item
if(m.flags.selected == s) return *this; // ignore if no change
m.flags.selected = s; // actually change selection
if (m.flags.selected != sel)
{
m.flags.selected = sel; // actually change selection
ess_->lister.emit_selected(this->pos_);
@ -4550,7 +4578,17 @@ namespace nana
else if (ess_->lister.last_selected_abs == pos_)
ess_->lister.last_selected_abs.set_both(npos);
if (scroll_view)
{
if (ess_->lister.get(pos_.cat)->expand)
ess_->lister.get(pos_.cat)->expand = false;
if (!this->displayed())
ess_->lister.scroll(pos_, !(ess_->scroll.offset_y_dpl > this->to_display()));
}
ess_->update();
}
return *this;
}

View File

@ -10,6 +10,7 @@
* @file: nana/gui/widgets/menu.cpp
* @contributors:
* kmribti(pr#102)
* dankan1890(pr#158)
*/
#include <nana/gui/widgets/menu.hpp>
@ -1116,9 +1117,9 @@ namespace nana
delete impl_;
}
auto menu::append(const std::string& text, const event_fn_t& callback) -> item_proxy
auto menu::append(std::string text_utf8, const menu::event_fn_t& callback) -> item_proxy
{
impl_->mbuilder.data().items.emplace_back(new item_type(text, callback));
impl_->mbuilder.data().items.emplace_back(new item_type(std::move(text_utf8), callback));
return item_proxy(size() - 1, *impl_->mbuilder.data().items.back());
}
@ -1156,6 +1157,11 @@ namespace nana
impl_->mbuilder.data().items.at(index)->image = img;
}
void menu::text(std::size_t index, std::string text_utf8)
{
impl_->mbuilder.data().items.at(index)->text.swap(text_utf8);
}
bool menu::link(std::size_t index, menu& menu_obj)
{
if(impl_->mbuilder.set_sub_menu(index, menu_obj.impl_->mbuilder.data()))

View File

@ -0,0 +1,262 @@
#ifndef NANA_PAINT_DETAIL_IMAGE_ICO_EX_HPP
#define NANA_PAINT_DETAIL_IMAGE_ICO_EX_HPP
#include "image_pixbuf.hpp"
namespace nana {
namespace paint {
namespace detail {
// These next two structs represent how the icon information is stored
// in an ICO file.
typedef struct
{
uint8_t bWidth; // Width of the image
uint8_t bHeight; // Height of the image (times 2)
uint8_t bColorCount; // Number of colors in image (0 if >=8bpp)
uint8_t bReserved; // Reserved
uint16_t wPlanes; // Color Planes
uint16_t wBitCount; // Bits per pixel
uint32_t dwBytesInRes; // how many bytes in this resource?
uint32_t dwImageOffset; // where in the file is this image
} ICONDIRENTRY, *LPICONDIRENTRY;
typedef struct
{
uint16_t idReserved; // Reserved
uint16_t idType; // resource type (1 for icons)
uint16_t idCount; // how many images?
//ICONDIRENTRY idEntries[1]; // the entries for each image
} ICONDIR, *LPICONDIR;
// size - 40 bytes
typedef struct
{
uint32_t biSize;
uint32_t biWidth;
uint32_t biHeight; // Icon Height (added height of XOR-Bitmap and AND-Bitmap)
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
int32_t biSizeImage;
uint32_t biXPelsPerMeter;
uint32_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
} s_BITMAPINFOHEADER, *s_PBITMAPINFOHEADER;
// 46 bytes
typedef struct
{
s_BITMAPINFOHEADER icHeader; // DIB header
uint32_t icColors[1]; // Color table (short 4 bytes) //RGBQUAD
uint8_t icXOR[1]; // DIB bits for XOR mask
uint8_t icAND[1]; // DIB bits for AND mask
} ICONIMAGE, *LPICONIMAGE;
class image_ico_ex
: public basic_image_pixbuf
{
bool _m_read_ico(const void* data, std::size_t size)
{
auto width = 0;
auto height = 0;
auto buffer = (unsigned char *)data;
auto icoDir = reinterpret_cast<LPICONDIR>(buffer);
int iconsCount = icoDir->idCount;
if (icoDir->idReserved != 0 || icoDir->idType != 1 || iconsCount == 0 || iconsCount > 20)
return false;
auto cursor = buffer;
cursor += 6;
auto dirEntry = reinterpret_cast<ICONDIRENTRY*>(cursor);
auto maxSize = 0;
auto offset = 0;
auto maxBitCount = 0;
for (auto i = 0; i < iconsCount; i++, ++dirEntry)
{
int w = dirEntry->bWidth;
int h = dirEntry->bHeight;
int bitCount = dirEntry->wBitCount;
if (w * h > maxSize || bitCount > maxBitCount) // we choose icon with max resolution
{
width = w;
height = h;
offset = dirEntry->dwImageOffset;
maxSize = w * h;
maxBitCount = bitCount;
}
}
if (offset == 0) return false;
cursor = buffer;
cursor += offset;
auto icon = reinterpret_cast<ICONIMAGE*>(cursor);
auto realBitsCount = static_cast<int>(icon->icHeader.biBitCount);
auto hasAndMask = (realBitsCount < 32) && (height != icon->icHeader.biHeight);
cursor += 40;
pixbuf_.open(width, height);
// rgba + vertical swap
if (realBitsCount >= 32)
{
for (auto x = 0; x < width; ++x)
for (auto y = 0; y < height; ++y)
{
pixbuf_.alpha_channel(true);
auto shift2 = 4 * (x + (height - y - 1) * width);
pixel_color_t image;
image.element.red = cursor[shift2 + 2];
image.element.green = cursor[shift2 + 1];
image.element.blue = cursor[shift2];
image.element.alpha_channel = cursor[shift2 + 3];
pixbuf_.pixel(x, y, image);
}
}
else if (realBitsCount == 24)
{
for (auto x = 0; x < width; x++)
for (auto y = 0; y < height; y++)
{
pixbuf_.alpha_channel(true);
auto shift2 = 3 * (x + (height - y - 1) * width);
pixel_color_t image;
image.element.red = cursor[shift2 + 2];
image.element.green = cursor[shift2 + 1];
image.element.blue = cursor[shift2];
image.element.alpha_channel = 255;
pixbuf_.pixel(x, y, image);
}
}
else if (realBitsCount == 8) /// 256 colors
{
// 256 color table
auto colors = reinterpret_cast<unsigned char *>(cursor);
cursor += 256 * 4;
for (auto x = 0; x < width; x++)
for (auto y = 0; y < height; y++)
{
pixbuf_.alpha_channel(true);
auto shift2 = (x + (height - y - 1) * width);
auto index = 4 * cursor[shift2];
pixel_color_t image;
image.element.red = colors[index + 2];
image.element.green = colors[index + 1];
image.element.blue = colors[index];
image.element.alpha_channel = 255;
pixbuf_.pixel(x, y, image);
}
}
else if (realBitsCount == 4) /// 16 colors
{
// 16 color table
auto colors = reinterpret_cast<unsigned char *>(cursor);
cursor += 16 * 4;
for (auto x = 0; x < width; x++)
for (auto y = 0; y < height; y++)
{
auto shift2 = (x + (height - y - 1) * width);
auto index = cursor[shift2 / 2];
if (shift2 % 2 == 0)
index = (index >> 4) & 0xF;
else
index = index & 0xF;
index *= 4;
pixbuf_.alpha_channel(true);
pixel_color_t image;
image.element.red = colors[index + 2];
image.element.green = colors[index + 1];
image.element.blue = colors[index];
image.element.alpha_channel = 255;
pixbuf_.pixel(x, y, image);
}
}
else if (realBitsCount == 1) /// 2 colors
{
// 2 color table
auto colors = reinterpret_cast<unsigned char *>(cursor);
cursor += 2 * 4;
auto boundary = width; //!!! 32 bit boundary (http://www.daubnet.com/en/file-format-ico)
while (boundary % 32 != 0) boundary++;
for (auto x = 0; x < width; x++)
for (auto y = 0; y < height; y++)
{
auto shift2 = (x + (height - y - 1) * boundary);
auto index = cursor[shift2 / 8];
// select 1 bit only
unsigned char bit = 7 - (x % 8);
index = (index >> bit) & 0x01;
index *= 4;
pixbuf_.alpha_channel(true);
pixel_color_t image;
image.element.red = colors[index + 2];
image.element.green = colors[index + 1];
image.element.blue = colors[index];
image.element.alpha_channel = 255;
pixbuf_.pixel(x, y, image);
}
}
// Read AND mask after base color data - 1 BIT MASK
if (hasAndMask)
{
auto boundary = width * realBitsCount; //!!! 32 bit boundary (http://www.daubnet.com/en/file-format-ico)
while (boundary % 32 != 0) boundary++;
cursor += boundary * height / 8;
boundary = width;
while (boundary % 32 != 0) boundary++;
for (auto y = 0; y < height; y++)
for (auto x = 0; x < width; x++)
{
unsigned char bit = 7 - (x % 8);
auto shift2 = (x + (height - y - 1) * boundary) / 8;
auto mask = (0x01 & (static_cast<unsigned char>(cursor[shift2]) >> bit));
auto pc = pixbuf_.pixel(x, y);
auto alpha = pc.element.alpha_channel;
alpha *= 1 - mask;
pc.element.alpha_channel = alpha;
pixbuf_.pixel(x, y, pc);
}
}
return true;
}
public:
bool open(const std::experimental::filesystem::path& ico_file) override
{
std::ifstream file(ico_file.string(), std::ios::binary);
if (!file.is_open()) return false;
// allocates a buffer for the image
file.seekg(0, std::ios::end);
const auto bytes = static_cast<std::size_t>(file.tellg());
file.seekg(0, std::ios::beg);
auto buffer = new char[bytes];
// read data from the file and set them in the buffer
file.read(buffer, bytes);
auto okret = _m_read_ico(buffer, bytes);
// delete buffer and return
delete[] buffer;
return okret;
}
bool open(const void* data, std::size_t bytes) override
{
return _m_read_ico(data, bytes);
}
};
}//end namespace detail
}//end namespace paint
}//end namespace nana
#endif

View File

@ -35,6 +35,7 @@
#include "detail/image_ico.hpp"
#include "image_accessor.hpp"
#include "detail/image_ico_ex.hpp"
namespace fs = std::experimental::filesystem;
@ -264,11 +265,7 @@ namespace paint
{
if (ext_ico == ext)
{
#if defined(NANA_WINDOWS)
ptr = std::make_shared<detail::image_ico>(true);
#else
return ptr;
#endif
ptr = std::make_shared<detail::image_ico_ex>();
break;
}
@ -367,6 +364,8 @@ namespace paint
ptr = std::make_shared<detail::image_ico>(true);
}
#endif
else if (!ptr && bytes > 40 && (0x00010000 == *reinterpret_cast<const unsigned*>(data)))
ptr = std::make_shared<detail::image_ico_ex>();
}
}