fix logic and crash errors of listbox box selection

This commit is contained in:
Jinhao 2017-06-17 10:48:24 +08:00
parent 8044cb0b34
commit 33321d73de

View File

@ -1507,36 +1507,22 @@ namespace nana
}
template<typename Pred>
std::vector<std::pair<index_pair, bool>> select_display_range_if(const index_pair& fr_abs, index_pair to_dpl, bool unselect_others, Pred pred)
std::vector<std::pair<index_pair, bool>> select_display_range_if(const index_pair& fr_abs, index_pair to_dpl, bool deselect_others, Pred pred)
{
const auto already_selected = this->pick_items(true);
const index_pairs already_selected = (deselect_others ? this->pick_items(true) : index_pairs{});
if (to_dpl.empty())
{
if (fr_abs.empty())
return{};
to_dpl = this->last();
}
auto fr_dpl = (fr_abs.is_category() ? fr_abs : this->index_cast(fr_abs, false)); //Converts an absolute position to display position
if (fr_dpl > to_dpl)
std::swap(fr_dpl, to_dpl);
if (to_dpl.is_category())
{
//Search backward until a last item of a category is found.
while (true)
{
auto msize = this->size_item(to_dpl.cat);
if (0 != msize)
{
to_dpl.item = msize - 1;
break;
}
if (fr_dpl.cat == to_dpl.cat)
break;
--(to_dpl.cat);
}
}
if (!this->good(to_dpl))
to_dpl = this->last();
const auto begin = fr_dpl;
const auto last = to_dpl;
@ -1576,9 +1562,9 @@ namespace nana
}
}
if (unselect_others)
if (deselect_others)
{
//Unselects the already selected which is out of range [begin, last]
//Deselects the already selected which is out of range [begin, last]
for (auto index : already_selected)
{
auto disp_order = this->index_cast(index, false); //converts an absolute position to a display position
@ -1935,6 +1921,7 @@ namespace nana
bool auto_draw{true};
bool checkable{false};
bool if_image{false};
bool deselect_deferred{ false }; //deselects items when mouse button is released.
unsigned text_height;
::nana::listbox::export_options def_exp_options;
@ -2059,16 +2046,19 @@ namespace nana
return{ r.x - origin.x, r.x - origin.x + static_cast<int>(header.pixels()) };
}
void start_mouse_selection(const point& screen_pos)
void start_mouse_selection(const arg_mouse& arg)
{
auto logic_pos = coordinate_cast(screen_pos, true);
auto logic_pos = coordinate_cast(arg.pos, true);
mouse_selection.started = true;
mouse_selection.begin_position = logic_pos;
mouse_selection.end_position = logic_pos;
mouse_selection.already_selected = lister.pick_items(true);
if (arg.ctrl || arg.shift)
{
mouse_selection.already_selected = lister.pick_items(true);
mouse_selection.reverse_selection = arg.ctrl;
}
API::set_capture(*listbox_ptr, true);
}
@ -2089,90 +2079,89 @@ namespace nana
mouse_selection.end_position = logic_pos;
bool cancel_selections = true;
auto content_x = coordinate_cast({ columns_range().first, 0 }, true).x;
if ((std::max)(mouse_selection.end_position.x, mouse_selection.begin_position.x) <= content_x ||
(std::min)(mouse_selection.end_position.x, mouse_selection.begin_position.x) >= content_x + static_cast<int>(header.pixels())
)
if ((std::max)(mouse_selection.end_position.x, mouse_selection.begin_position.x) >= content_x &&
(std::min)(mouse_selection.end_position.x, mouse_selection.begin_position.x) < content_x + static_cast<int>(header.pixels()))
{
lister.select_for_all(false);
return;
}
auto const begin_off = (std::max)((std::min)(mouse_selection.begin_position.y, mouse_selection.end_position.y), 0) / item_height();
auto const begin_off = (std::max)((std::min)(mouse_selection.begin_position.y, mouse_selection.end_position.y), 0) / item_height();
auto begin = lister.advance(lister.first(), begin_off);
if (begin.empty())
return;
std::vector<std::pair<index_pair, bool>> selections;
if ((mouse_selection.end_position.y < 0) || (lister.distance(lister.first(), begin) == begin_off))
{
//The range [begin_off, last_off] is a range of box selection
auto last_off = (std::max)(mouse_selection.begin_position.y, mouse_selection.end_position.y) / item_height();
auto last = lister.advance(lister.first(), last_off);
selections = lister.select_display_range_if(begin, last, false, [this](const index_pair& abs_pos) {
if (this->mouse_selection.reverse_selection)
{
if(mouse_selection.already_selected.cend() != std::find(mouse_selection.already_selected.cbegin(), mouse_selection.already_selected.cend(), abs_pos))
{
item_proxy{ this, abs_pos }.select(false);
return false;
}
}
return true;
});
for (auto & pair : selections)
auto begin = lister.advance(lister.first(), begin_off);
if (!begin.empty())
{
if (pair.second)
continue;
std::vector<std::pair<index_pair, bool>> selections;
if (mouse_selection.selections.cend() ==
std::find(mouse_selection.selections.cbegin(), mouse_selection.selections.cend(), pair.first))
if ((mouse_selection.end_position.y < 0) || (lister.distance(lister.first(), begin) == begin_off))
{
mouse_selection.selections.push_back(pair.first);
}
}
//The range [begin_off, last_off] is a range of box selection
auto last_off = (std::max)(mouse_selection.begin_position.y, mouse_selection.end_position.y) / item_height();
auto last = lister.advance(lister.first(), last_off);
selections = lister.select_display_range_if(begin, last, false, [this](const index_pair& abs_pos) {
if (this->mouse_selection.reverse_selection)
{
if (mouse_selection.already_selected.cend() != std::find(mouse_selection.already_selected.cbegin(), mouse_selection.already_selected.cend(), abs_pos))
{
item_proxy{ this, abs_pos }.select(false);
return false;
}
}
return true;
});
for (auto & pair : selections)
{
if (pair.second)
continue;
if (mouse_selection.selections.cend() ==
std::find(mouse_selection.selections.cbegin(), mouse_selection.selections.cend(), pair.first))
{
mouse_selection.selections.push_back(pair.first);
}
}
#ifdef _MSC_VER
for(auto i = mouse_selection.selections.cbegin(); i != mouse_selection.selections.cend();)
for (auto i = mouse_selection.selections.cbegin(); i != mouse_selection.selections.cend();)
#else
for(auto i = mouse_selection.selections.begin(); i != mouse_selection.selections.end();)
for(auto i = mouse_selection.selections.begin(); i != mouse_selection.selections.end();)
#endif
{
if (selections.cend() == std::find_if(selections.cbegin(), selections.cend(), pred_mouse_selection{*i}))
{
item_proxy{ this, *i }.select(false);
i = mouse_selection.selections.erase(i);
}
else
++i;
}
{
if (selections.cend() == std::find_if(selections.cbegin(), selections.cend(), pred_mouse_selection{ *i }))
{
item_proxy{ this, *i }.select(false);
i = mouse_selection.selections.erase(i);
}
else
++i;
}
}
else
{
for (auto & pos : mouse_selection.selections)
{
item_proxy{ this, pos }.select(false);
cancel_selections = false;
}
}
}
if (cancel_selections)
{
if (!mouse_selection.already_selected.empty())
{
for (auto & pos : mouse_selection.selections)
item_proxy(this, pos).select(false);
//Don't restore the already selections if it is reverse selection(pressing shift). Behaves like Windows Explorer.
if (!mouse_selection.reverse_selection)
{
for (auto & abs_pos : mouse_selection.already_selected)
item_proxy(this, abs_pos).select(true);
}
}
else
lister.select_for_all(false);
mouse_selection.selections.clear();
}
if (mouse_selection.reverse_selection)
{
for (auto & abs_pos : mouse_selection.already_selected)
{
if (selections.empty() || (selections.cend() == std::find_if(selections.cbegin(), selections.cend(), pred_mouse_selection{ abs_pos })))
{
item_proxy{ this, abs_pos }.select(true);
}
}
}
}
void stop_mouse_selection() noexcept
@ -3934,6 +3923,9 @@ namespace nana
using item_state = essence::item_state;
using parts = essence::parts;
//Cancel deferred deselection operation when mouse moves.
essence_->deselect_deferred = false;
bool need_refresh = false;
point pos_in_header = arg.pos;
@ -4153,23 +4145,6 @@ namespace nana
}
update = true;
}
else
{
//Blank area is clicked
bool unselect_all = true;
if (!lister.single_status(true)) //not single selected
{
if (arg.ctrl || arg.shift)
{
essence_->mouse_selection.reverse_selection = arg.ctrl;
unselect_all = false;
}
}
if(unselect_all)
update = lister.select_for_all(false); //unselect all items due to the blank area being clicked
}
if(update)
{
@ -4187,16 +4162,11 @@ namespace nana
{
//Start box selection if mulit-selection is enabled
if (arg.is_left_button() && (!lister.single_status(true)))
essence_->start_mouse_selection(arg.pos);
essence_->start_mouse_selection(arg);
lister.select_for_all(false);
update = true;
if (good_list_r)
{
drawer_lister_->draw(list_r);
if (good_head_r)
drawer_header_->draw(graph, head_r);
}
//Deselection of all items is deferred to the mouse up event when ctrl or shift is not pressed
//Pressing ctrl or shift is to selects other items without deselecting current selections.
essence_->deselect_deferred = !(arg.ctrl || arg.shift);
}
if(update)
@ -4236,6 +4206,12 @@ namespace nana
need_refresh = true;
}
if (essence_->deselect_deferred)
{
essence_->deselect_deferred = false;
need_refresh |= (essence_->lister.select_for_all(false));
}
if (need_refresh)
{
refresh(graph);