diff --git a/source/gui/place.cpp b/source/gui/place.cpp index 205045f5..98c698c1 100644 --- a/source/gui/place.cpp +++ b/source/gui/place.cpp @@ -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 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 _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 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{ 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); } } }