add support of div-text update for splitter(#157)

This commit is contained in:
Jinhao 2016-10-05 09:41:44 +08:00
parent 7f45ae4342
commit 5383d85cc0

View File

@ -431,6 +431,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
{
@ -1315,8 +1396,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);
@ -1341,7 +1423,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)
@ -1374,6 +1456,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)
{
@ -1472,6 +1555,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();
@ -1532,6 +1840,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_;
@ -2171,7 +2480,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 });
}
@ -2726,95 +3035,26 @@ namespace nana
return *p;
}
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;
}
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;
//Get the bounds of this field
std::size_t begin = 0, end = 0;
//Find the begin
int level = 0;
std::size_t pos = fieldname_pos;
while (true)
{
pos = div.find_last_of("<>", pos);
if (div.npos == pos)
return;
if ('<' == div[pos])
{
if (0 == level)
{
begin = pos;
break;
}
else
--level;
}
else
++level;
}
//Find the end
level = 0;
pos = fieldname_pos;
while (true)
{
pos = div.find_first_of("<>", pos);
if (div.npos == pos)
return;
if ('>' == div[pos])
{
if (0 == level)
{
end = pos + 1;
break;
}
else
--level;
}
else
++level;
}
auto fieldstr = div.substr(begin + 1, end - begin - 2);
auto bound = get_field_bound(div, field, 0);
auto fieldstr = div.substr(bound.first + 1, bound.second - bound.first - 2);
//Search the attribute
for (pos = 0; true; ++pos)
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.
level = 0;
depth = 0;
std::size_t off = pos;
while (true)
{
@ -2823,16 +3063,16 @@ namespace nana
break;
if ('>' == fieldstr[off])
++level;
++depth;
else
--level;
--depth;
if (0 == off)
break;
--off;
}
if (0 == level)
if (0 == depth)
break;
}
@ -2843,8 +3083,8 @@ namespace nana
div.insert(fieldname_pos + std::strlen(field), " " + std::string(attr));
else if (operation == update_operation::replace)
{
div.erase(begin + 1, fieldstr.length());
div.insert(begin + 1, std::string(attr) + " " + std::string(field));
div.erase(bound.first + 1, fieldstr.length());
div.insert(bound.first + 1, std::string(attr) + " " + std::string(field));
}
}
else
@ -2852,10 +3092,10 @@ namespace nana
//There is an attribute
if (operation == update_operation::erase)
{
div.erase(begin + pos + 1, std::strlen(attr));
div.erase(bound.first + pos + 1, std::strlen(attr));
if ((div[begin + pos] == div[begin + pos + 1]) && (' ' == div[begin + pos]))
div.erase(begin + pos, 1);
if ((div[bound.first + pos] == div[bound.first + pos + 1]) && (' ' == div[bound.first + pos]))
div.erase(bound.first + pos, 1);
}
}
}