Serenity Operating System
at master 291 lines 9.2 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/QuickSort.h> 8#include <LibGUI/AbstractView.h> 9#include <LibGUI/SortingProxyModel.h> 10 11namespace GUI { 12 13SortingProxyModel::SortingProxyModel(NonnullRefPtr<Model> source) 14 : m_source(move(source)) 15{ 16 m_source->register_client(*this); 17 update_sort(); 18} 19 20SortingProxyModel::~SortingProxyModel() 21{ 22 m_source->unregister_client(*this); 23} 24 25void SortingProxyModel::invalidate() 26{ 27 source().invalidate(); 28 Model::invalidate(); 29} 30 31void SortingProxyModel::update_sort(unsigned flags) 32{ 33 if (flags == UpdateFlag::DontInvalidateIndices) { 34 sort(m_last_key_column, m_last_sort_order); 35 } else { 36 m_mappings.clear(); 37 38 // FIXME: This is really harsh, but without precise invalidation, not much we can do. 39 for_each_view([&](auto& view) { 40 view.set_cursor({}, AbstractView::SelectionUpdate::None); 41 view.selection().clear(); 42 }); 43 } 44 did_update(flags); 45} 46 47void SortingProxyModel::model_did_update(unsigned flags) 48{ 49 update_sort(flags); 50} 51 52bool SortingProxyModel::accepts_drag(ModelIndex const& proxy_index, Vector<DeprecatedString> const& mime_types) const 53{ 54 return source().accepts_drag(map_to_source(proxy_index), mime_types); 55} 56 57int SortingProxyModel::row_count(ModelIndex const& proxy_index) const 58{ 59 return source().row_count(map_to_source(proxy_index)); 60} 61 62int SortingProxyModel::column_count(ModelIndex const& proxy_index) const 63{ 64 return source().column_count(map_to_source(proxy_index)); 65} 66 67ModelIndex SortingProxyModel::map_to_source(ModelIndex const& proxy_index) const 68{ 69 if (!proxy_index.is_valid()) 70 return {}; 71 72 VERIFY(proxy_index.model() == this); 73 VERIFY(proxy_index.internal_data()); 74 75 auto& index_mapping = *static_cast<Mapping*>(proxy_index.internal_data()); 76 auto it = m_mappings.find(index_mapping.source_parent); 77 VERIFY(it != m_mappings.end()); 78 79 auto& mapping = *it->value; 80 if (static_cast<size_t>(proxy_index.row()) >= mapping.source_rows.size() || proxy_index.column() >= column_count()) 81 return {}; 82 int source_row = mapping.source_rows[proxy_index.row()]; 83 int source_column = proxy_index.column(); 84 return source().index(source_row, source_column, it->key); 85} 86 87ModelIndex SortingProxyModel::map_to_proxy(ModelIndex const& source_index) const 88{ 89 if (!source_index.is_valid()) 90 return {}; 91 92 VERIFY(source_index.model() == m_source); 93 94 auto source_parent = source_index.parent(); 95 auto it = const_cast<SortingProxyModel*>(this)->build_mapping(source_parent); 96 97 auto& mapping = *it->value; 98 99 if (source_index.row() >= static_cast<int>(mapping.proxy_rows.size()) || source_index.column() >= column_count()) 100 return {}; 101 102 int proxy_row = mapping.proxy_rows[source_index.row()]; 103 int proxy_column = source_index.column(); 104 if (proxy_row < 0 || proxy_column < 0) 105 return {}; 106 return create_index(proxy_row, proxy_column, &mapping); 107} 108 109DeprecatedString SortingProxyModel::column_name(int column) const 110{ 111 return source().column_name(column); 112} 113 114Variant SortingProxyModel::data(ModelIndex const& proxy_index, ModelRole role) const 115{ 116 return source().data(map_to_source(proxy_index), role); 117} 118 119StringView SortingProxyModel::drag_data_type() const 120{ 121 return source().drag_data_type(); 122} 123 124bool SortingProxyModel::less_than(ModelIndex const& index1, ModelIndex const& index2) const 125{ 126 auto data1 = index1.data(m_sort_role); 127 auto data2 = index2.data(m_sort_role); 128 if (data1.is_string() && data2.is_string()) 129 return data1.as_string().to_lowercase() < data2.as_string().to_lowercase(); 130 return data1 < data2; 131} 132 133ModelIndex SortingProxyModel::index(int row, int column, ModelIndex const& parent) const 134{ 135 if (row < 0 || column < 0) 136 return {}; 137 138 auto source_parent = map_to_source(parent); 139 const_cast<SortingProxyModel*>(this)->build_mapping(source_parent); 140 141 auto it = m_mappings.find(source_parent); 142 VERIFY(it != m_mappings.end()); 143 auto& mapping = *it->value; 144 if (row >= static_cast<int>(mapping.source_rows.size()) || column >= column_count()) 145 return {}; 146 return create_index(row, column, &mapping); 147} 148 149ModelIndex SortingProxyModel::parent_index(ModelIndex const& proxy_index) const 150{ 151 if (!proxy_index.is_valid()) 152 return {}; 153 154 VERIFY(proxy_index.model() == this); 155 VERIFY(proxy_index.internal_data()); 156 157 auto& index_mapping = *static_cast<Mapping*>(proxy_index.internal_data()); 158 auto it = m_mappings.find(index_mapping.source_parent); 159 VERIFY(it != m_mappings.end()); 160 161 return map_to_proxy(it->value->source_parent); 162} 163 164void SortingProxyModel::sort_mapping(Mapping& mapping, int column, SortOrder sort_order) 165{ 166 auto old_source_rows = mapping.source_rows; 167 168 int row_count = source().row_count(mapping.source_parent); 169 mapping.source_rows.resize(row_count); 170 mapping.proxy_rows.resize(row_count); 171 172 if (column == -1) { 173 for (int i = 0; i < row_count; ++i) { 174 mapping.source_rows[i] = i; 175 mapping.proxy_rows[i] = i; 176 } 177 return; 178 } 179 180 for (int i = 0; i < row_count; ++i) 181 mapping.source_rows[i] = i; 182 183 quick_sort(mapping.source_rows, [&](auto row1, auto row2) -> bool { 184 bool is_less_than = less_than(source().index(row1, column, mapping.source_parent), source().index(row2, column, mapping.source_parent)); 185 return sort_order == SortOrder::Ascending ? is_less_than : !is_less_than; 186 }); 187 188 for (int i = 0; i < row_count; ++i) 189 mapping.proxy_rows[mapping.source_rows[i]] = i; 190 191 // FIXME: I really feel like this should be done at the view layer somehow. 192 for_each_view([&](AbstractView& view) { 193 // Update the view's selection. 194 view.selection().change_from_model({}, [&](ModelSelection& selection) { 195 Vector<ModelIndex> selected_indices_in_source; 196 Vector<ModelIndex> stale_indices_in_selection; 197 selection.for_each_index([&](ModelIndex const& index) { 198 if (index.parent() == mapping.source_parent) { 199 stale_indices_in_selection.append(index); 200 selected_indices_in_source.append(source().index(old_source_rows[index.row()], index.column(), mapping.source_parent)); 201 } 202 }); 203 204 for (auto& index : stale_indices_in_selection) { 205 selection.remove(index); 206 } 207 208 for (auto& index : selected_indices_in_source) { 209 for (size_t i = 0; i < mapping.source_rows.size(); ++i) { 210 if (mapping.source_rows[i] == index.row()) { 211 auto new_source_index = this->index(i, index.column(), mapping.source_parent); 212 selection.add(new_source_index); 213 // Update the view's cursor. 214 auto cursor = view.cursor_index(); 215 if (cursor.is_valid() && cursor.parent() == mapping.source_parent) 216 view.set_cursor(new_source_index, AbstractView::SelectionUpdate::None, false); 217 break; 218 } 219 } 220 } 221 }); 222 }); 223} 224 225void SortingProxyModel::sort(int column, SortOrder sort_order) 226{ 227 for (auto& it : m_mappings) { 228 auto& mapping = *it.value; 229 sort_mapping(mapping, column, sort_order); 230 } 231 232 m_last_key_column = column; 233 m_last_sort_order = sort_order; 234 235 did_update(UpdateFlag::DontInvalidateIndices); 236} 237 238SortingProxyModel::InternalMapIterator SortingProxyModel::build_mapping(ModelIndex const& source_parent) 239{ 240 auto it = m_mappings.find(source_parent); 241 if (it != m_mappings.end()) 242 return it; 243 244 auto mapping = make<Mapping>(); 245 246 mapping->source_parent = source_parent; 247 248 int row_count = source().row_count(source_parent); 249 mapping->source_rows.resize(row_count); 250 mapping->proxy_rows.resize(row_count); 251 252 sort_mapping(*mapping, m_last_key_column, m_last_sort_order); 253 254 if (source_parent.is_valid()) { 255 auto source_grand_parent = source_parent.parent(); 256 build_mapping(source_grand_parent); 257 } 258 259 m_mappings.set(source_parent, move(mapping)); 260 return m_mappings.find(source_parent); 261} 262 263bool SortingProxyModel::is_column_sortable(int column_index) const 264{ 265 return source().is_column_sortable(column_index); 266} 267 268bool SortingProxyModel::is_editable(ModelIndex const& proxy_index) const 269{ 270 return source().is_editable(map_to_source(proxy_index)); 271} 272 273void SortingProxyModel::set_data(ModelIndex const& proxy_index, Variant const& data) 274{ 275 source().set_data(map_to_source(proxy_index), data); 276} 277 278bool SortingProxyModel::is_searchable() const 279{ 280 return source().is_searchable(); 281} 282 283Vector<ModelIndex> SortingProxyModel::matches(StringView searching, unsigned flags, ModelIndex const& proxy_index) 284{ 285 auto found_indices = source().matches(searching, flags, map_to_source(proxy_index)); 286 for (size_t i = 0; i < found_indices.size(); i++) 287 found_indices[i] = map_to_proxy(found_indices[i]); 288 return found_indices; 289} 290 291}