diff --git a/Libraries/LibGUI/SortingProxyModel.cpp b/Libraries/LibGUI/SortingProxyModel.cpp index 4a5be37c72..e2144f8bfd 100644 --- a/Libraries/LibGUI/SortingProxyModel.cpp +++ b/Libraries/LibGUI/SortingProxyModel.cpp @@ -25,11 +25,8 @@ */ #include -#include #include #include -#include -#include namespace GUI { @@ -37,12 +34,8 @@ SortingProxyModel::SortingProxyModel(NonnullRefPtr source) : m_source(move(source)) , m_key_column(-1) { - // Since the source model already called Model::did_update we can't - // assume we will get another call. So, we need to register for further - // updates and just call resort() right away, otherwise requests - // to this model won't work because there are no indices to map m_source->register_client(*this); - resort(); + invalidate(); } SortingProxyModel::~SortingProxyModel() @@ -50,30 +43,67 @@ SortingProxyModel::~SortingProxyModel() m_source->unregister_client(*this); } +void SortingProxyModel::invalidate(unsigned int flags) +{ + m_mappings.clear(); + did_update(flags); +} + void SortingProxyModel::model_did_update(unsigned flags) { - resort(flags); + invalidate(flags); } -int SortingProxyModel::row_count(const ModelIndex& index) const +int SortingProxyModel::row_count(const ModelIndex& proxy_index) const { - auto source_index = map_to_source(index); - return source().row_count(source_index); + return source().row_count(map_to_source(proxy_index)); } -int SortingProxyModel::column_count(const ModelIndex& index) const +int SortingProxyModel::column_count(const ModelIndex& proxy_index) const { - auto source_index = map_to_source(index); - return source().column_count(source_index); + return source().column_count(map_to_source(proxy_index)); } -ModelIndex SortingProxyModel::map_to_source(const ModelIndex& index) const +ModelIndex SortingProxyModel::map_to_source(const ModelIndex& proxy_index) const { - if (!index.is_valid()) + if (!proxy_index.is_valid()) return {}; - if (static_cast(index.row()) >= m_row_mappings.size() || index.column() >= column_count()) + + ASSERT(proxy_index.model() == this); + ASSERT(proxy_index.internal_data()); + + auto& index_mapping = *static_cast(proxy_index.internal_data()); + auto it = m_mappings.find(index_mapping.source_parent); + ASSERT(it != m_mappings.end()); + + auto& mapping = *it->value; + if (static_cast(proxy_index.row()) >= mapping.source_rows.size() || proxy_index.column() >= column_count()) return {}; - return source().index(m_row_mappings[index.row()], index.column()); + int source_row = mapping.source_rows[proxy_index.row()]; + int source_column = proxy_index.column(); + return source().index(source_row, source_column, it->key); +} + +ModelIndex SortingProxyModel::map_to_proxy(const ModelIndex& source_index) const +{ + if (!source_index.is_valid()) + return {}; + + ASSERT(source_index.model() == m_source); + + auto source_parent = source_index.parent(); + auto it = const_cast(this)->build_mapping(source_parent); + + auto& mapping = *it->value; + + if (source_index.row() >= static_cast(mapping.proxy_rows.size()) || source_index.column() >= column_count()) + return {}; + + int proxy_row = mapping.proxy_rows[source_index.row()]; + int proxy_column = source_index.column(); + if (proxy_row < 0 || proxy_column < 0) + return {}; + return create_index(proxy_row, proxy_column, &mapping); } String SortingProxyModel::column_name(int column) const @@ -81,11 +111,9 @@ String SortingProxyModel::column_name(int column) const return source().column_name(column); } -Variant SortingProxyModel::data(const ModelIndex& index, Role role) const +Variant SortingProxyModel::data(const ModelIndex& proxy_index, Role role) const { - auto source_index = map_to_source(index); - ASSERT(source_index.is_valid()); - return source().data(source_index, role); + return source().data(map_to_source(proxy_index), role); } void SortingProxyModel::update() @@ -106,52 +134,89 @@ void SortingProxyModel::set_key_column_and_sort_order(int column, SortOrder sort ASSERT(column >= 0 && column < column_count()); m_key_column = column; m_sort_order = sort_order; - resort(); + invalidate(); } bool SortingProxyModel::less_than(const ModelIndex& index1, const ModelIndex& index2) const { - auto data1 = data(index1, Role::Sort); - auto data2 = data(index2, Role::Sort); + auto data1 = index1.model() ? index1.model()->data(index1, m_sort_role) : Variant(); + auto data2 = index2.model() ? index2.model()->data(index2, m_sort_role) : Variant(); if (data1.is_string() && data2.is_string()) return data1.as_string().to_lowercase() < data2.as_string().to_lowercase(); return data1 < data2; } -void SortingProxyModel::resort(unsigned flags) +ModelIndex SortingProxyModel::index(int row, int column, const ModelIndex& parent) const { - auto old_row_mappings = m_row_mappings; - int row_count = source().row_count(); - m_row_mappings.resize(row_count); - for (int i = 0; i < row_count; ++i) - m_row_mappings[i] = i; - if (m_key_column == -1) { - did_update(flags); - return; + if (row < 0 || column < 0) + return {}; + + auto source_parent = map_to_source(parent); + const_cast(this)->build_mapping(source_parent); + + auto it = m_mappings.find(source_parent); + ASSERT(it != m_mappings.end()); + auto& mapping = *it->value; + if (row >= static_cast(mapping.source_rows.size()) || column >= column_count()) + return {}; + return create_index(row, column, &mapping); +} + +ModelIndex SortingProxyModel::parent_index(const ModelIndex& proxy_index) const +{ + if (!proxy_index.is_valid()) + return {}; + + ASSERT(proxy_index.model() == this); + ASSERT(proxy_index.internal_data()); + + auto& index_mapping = *static_cast(proxy_index.internal_data()); + auto it = m_mappings.find(index_mapping.source_parent); + ASSERT(it != m_mappings.end()); + + return map_to_proxy(it->value->source_parent); +} + +SortingProxyModel::InternalMapIterator SortingProxyModel::build_mapping(const ModelIndex& source_parent) +{ + auto it = m_mappings.find(source_parent); + if (it != m_mappings.end()) + return it; + + auto mapping = make(); + + mapping->source_parent = source_parent; + + int row_count = source().row_count(source_parent); + mapping->source_rows.resize(row_count); + mapping->proxy_rows.resize(row_count); + + for (int i = 0; i < row_count; ++i) { + mapping->source_rows[i] = i; } - quick_sort(m_row_mappings, [&](auto row1, auto row2) -> bool { - bool is_less_than = less_than(source().index(row1, m_key_column), source().index(row2, m_key_column)); + + // If we don't have a key column, we're not sorting. + if (m_key_column == -1) { + m_mappings.set(source_parent, move(mapping)); + return m_mappings.find(source_parent); + } + + quick_sort(mapping->source_rows, [&](auto row1, auto row2) -> bool { + bool is_less_than = less_than(source().index(row1, m_key_column, source_parent), source().index(row2, m_key_column, source_parent)); return m_sort_order == SortOrder::Ascending ? is_less_than : !is_less_than; }); - for_each_view([&](AbstractView& view) { - view.selection().change_from_model({}, [&](ModelSelection& selection) { - Vector selected_indexes_in_source; - selection.for_each_index([&](const ModelIndex& index) { - selected_indexes_in_source.append(source().index(old_row_mappings[index.row()], index.column())); - }); - selection.clear(); - for (auto& index : selected_indexes_in_source) { - for (size_t i = 0; i < m_row_mappings.size(); ++i) { - if (m_row_mappings[i] == index.row()) { - selection.add(this->index(i, index.column())); - continue; - } - } - } - }); - }); - did_update(flags); + for (int i = 0; i < row_count; ++i) { + mapping->proxy_rows[mapping->source_rows[i]] = i; + } + + if (source_parent.is_valid()) { + auto source_grand_parent = source_parent.parent(); + build_mapping(source_grand_parent); + } + + m_mappings.set(source_parent, move(mapping)); + return m_mappings.find(source_parent); } bool SortingProxyModel::is_column_sortable(int column_index) const diff --git a/Libraries/LibGUI/SortingProxyModel.h b/Libraries/LibGUI/SortingProxyModel.h index 9e2323a03c..1f50d4f039 100644 --- a/Libraries/LibGUI/SortingProxyModel.h +++ b/Libraries/LibGUI/SortingProxyModel.h @@ -43,6 +43,8 @@ public: virtual Variant data(const ModelIndex&, Role = Role::Display) const override; virtual void update() override; virtual StringView drag_data_type() const override; + virtual ModelIndex parent_index(const ModelIndex&) const override; + virtual ModelIndex index(int row, int column, const ModelIndex& parent) const override; virtual int key_column() const override { return m_key_column; } virtual SortOrder sort_order() const override { return m_sort_order; } @@ -52,6 +54,7 @@ public: virtual bool less_than(const ModelIndex&, const ModelIndex&) const; ModelIndex map_to_source(const ModelIndex&) const; + ModelIndex map_to_proxy(const ModelIndex&) const; Role sort_role() const { return m_sort_role; } void set_sort_role(Role role) { m_sort_role = role; } @@ -59,16 +62,27 @@ public: private: explicit SortingProxyModel(NonnullRefPtr source); + // NOTE: The internal_data() of indexes points to the corresponding Mapping object for that index. + struct Mapping { + Vector source_rows; + Vector proxy_rows; + ModelIndex source_parent; + }; + + using InternalMapIterator = HashMap>::IteratorType; + // ^ModelClient virtual void model_did_update(unsigned) override; Model& source() { return *m_source; } const Model& source() const { return *m_source; } - void resort(unsigned flags = Model::UpdateFlag::DontInvalidateIndexes); + void invalidate(unsigned flags = Model::UpdateFlag::DontInvalidateIndexes); + InternalMapIterator build_mapping(const ModelIndex& proxy_index); NonnullRefPtr m_source; - Vector m_row_mappings; + + HashMap> m_mappings; int m_key_column { -1 }; SortOrder m_sort_order { SortOrder::Ascending }; Role m_sort_role { Role::Sort };