Serenity Operating System
at master 486 lines 16 kB view raw
1/* 2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com> 4 * Copyright (c) 2022, the SerenityOS developers. 5 * 6 * SPDX-License-Identifier: BSD-2-Clause 7 */ 8 9#include <LibGUI/AbstractView.h> 10#include <LibGUI/Model.h> 11#include <LibGUI/PersistentModelIndex.h> 12 13namespace GUI { 14 15Model::Model() = default; 16 17Model::~Model() = default; 18 19void Model::register_view(Badge<AbstractView>, AbstractView& view) 20{ 21 m_views.set(&view); 22 m_clients.set(&view); 23} 24 25void Model::unregister_view(Badge<AbstractView>, AbstractView& view) 26{ 27 m_views.remove(&view); 28 m_clients.remove(&view); 29} 30 31void Model::invalidate() 32{ 33 m_persistent_handles.clear(); 34 did_update(); 35} 36 37void Model::for_each_view(Function<void(AbstractView&)> callback) 38{ 39 for (auto* view : m_views) 40 callback(*view); 41} 42 43void Model::for_each_client(Function<void(ModelClient&)> callback) 44{ 45 for (auto* client : m_clients) 46 callback(*client); 47} 48 49void Model::did_update(unsigned flags) 50{ 51 for_each_client([flags](ModelClient& client) { 52 client.model_did_update(flags); 53 }); 54} 55 56ModelIndex Model::create_index(int row, int column, void const* data) const 57{ 58 return ModelIndex(*this, row, column, const_cast<void*>(data)); 59} 60 61ModelIndex Model::index(int row, int column, ModelIndex const&) const 62{ 63 return create_index(row, column); 64} 65 66bool Model::accepts_drag(ModelIndex const&, Vector<DeprecatedString> const&) const 67{ 68 return false; 69} 70 71void Model::register_client(ModelClient& client) 72{ 73 m_clients.set(&client); 74} 75 76void Model::unregister_client(ModelClient& client) 77{ 78 m_clients.remove(&client); 79} 80 81WeakPtr<PersistentHandle> Model::register_persistent_index(Badge<PersistentModelIndex>, ModelIndex const& index) 82{ 83 if (!index.is_valid()) 84 return {}; 85 86 auto it = m_persistent_handles.find(index); 87 // Easy modo: we already have a handle for this model index. 88 if (it != m_persistent_handles.end()) { 89 return it->value->make_weak_ptr(); 90 } 91 92 // Hard modo: create a new persistent handle. 93 auto handle = adopt_own(*new PersistentHandle(index)); 94 auto weak_handle = handle->make_weak_ptr(); 95 m_persistent_handles.set(index, move(handle)); 96 97 return weak_handle; 98} 99 100RefPtr<Core::MimeData> Model::mime_data(ModelSelection const& selection) const 101{ 102 auto mime_data = Core::MimeData::construct(); 103 RefPtr<Gfx::Bitmap const> bitmap; 104 105 StringBuilder text_builder; 106 StringBuilder data_builder; 107 bool first = true; 108 selection.for_each_index([&](auto& index) { 109 auto text_data = index.data(); 110 if (!first) 111 text_builder.append(", "sv); 112 text_builder.append(text_data.to_deprecated_string()); 113 114 if (!first) 115 data_builder.append('\n'); 116 auto data = index.data(ModelRole::MimeData); 117 data_builder.append(data.to_deprecated_string()); 118 119 first = false; 120 121 if (!bitmap) { 122 Variant icon_data = index.data(ModelRole::Icon); 123 if (icon_data.is_icon()) 124 bitmap = icon_data.as_icon().bitmap_for_size(32); 125 } 126 }); 127 128 mime_data->set_data(drag_data_type(), data_builder.to_byte_buffer().release_value_but_fixme_should_propagate_errors()); 129 mime_data->set_text(text_builder.to_deprecated_string()); 130 if (bitmap) 131 mime_data->set_data("image/x-raw-bitmap", bitmap->serialize_to_byte_buffer().release_value_but_fixme_should_propagate_errors()); 132 133 return mime_data; 134} 135 136void Model::begin_insert_rows(ModelIndex const& parent, int first, int last) 137{ 138 VERIFY(first >= 0); 139 VERIFY(first <= last); 140 m_operation_stack.empend(OperationType::Insert, Direction::Row, parent, first, last); 141} 142 143void Model::begin_insert_columns(ModelIndex const& parent, int first, int last) 144{ 145 VERIFY(first >= 0); 146 VERIFY(first <= last); 147 m_operation_stack.empend(OperationType::Insert, Direction::Column, parent, first, last); 148} 149 150void Model::begin_move_rows(ModelIndex const& source_parent, int first, int last, ModelIndex const& target_parent, int target_index) 151{ 152 VERIFY(first >= 0); 153 VERIFY(first <= last); 154 VERIFY(target_index >= 0); 155 m_operation_stack.empend(OperationType::Move, Direction::Row, source_parent, first, last, target_parent, target_index); 156} 157 158void Model::begin_move_columns(ModelIndex const& source_parent, int first, int last, ModelIndex const& target_parent, int target_index) 159{ 160 VERIFY(first >= 0); 161 VERIFY(first <= last); 162 VERIFY(target_index >= 0); 163 m_operation_stack.empend(OperationType::Move, Direction::Column, source_parent, first, last, target_parent, target_index); 164} 165 166void Model::begin_delete_rows(ModelIndex const& parent, int first, int last) 167{ 168 VERIFY(first >= 0); 169 VERIFY(first <= last); 170 VERIFY(last < row_count(parent)); 171 172 save_deleted_indices<true>(parent, first, last); 173 m_operation_stack.empend(OperationType::Delete, Direction::Row, parent, first, last); 174} 175 176void Model::begin_delete_columns(ModelIndex const& parent, int first, int last) 177{ 178 VERIFY(first >= 0); 179 VERIFY(first <= last); 180 VERIFY(last < column_count(parent)); 181 182 save_deleted_indices<false>(parent, first, last); 183 m_operation_stack.empend(OperationType::Delete, Direction::Column, parent, first, last); 184} 185 186template<bool IsRow> 187void Model::save_deleted_indices(ModelIndex const& parent, int first, int last) 188{ 189 Vector<ModelIndex> deleted_indices; 190 191 for (auto& entry : m_persistent_handles) { 192 auto current_index = entry.key; 193 194 // Walk up the persistent handle's parents to see if it is contained 195 // within the range that is being deleted. 196 while (current_index.is_valid()) { 197 auto current_parent = current_index.parent(); 198 199 if (current_parent == parent) { 200 if constexpr (IsRow) { 201 if (current_index.row() >= first && current_index.row() <= last) 202 deleted_indices.append(current_index); 203 } else { 204 if (current_index.column() >= first && current_index.column() <= last) 205 deleted_indices.append(current_index); 206 } 207 } 208 209 current_index = current_parent; 210 } 211 } 212 213 m_deleted_indices_stack.append(move(deleted_indices)); 214} 215 216void Model::end_insert_rows() 217{ 218 auto operation = m_operation_stack.take_last(); 219 VERIFY(operation.type == OperationType::Insert); 220 VERIFY(operation.direction == Direction::Row); 221 handle_insert(operation); 222 223 for_each_client([&operation](ModelClient& client) { 224 client.model_did_insert_rows(operation.source_parent, operation.first, operation.last); 225 }); 226} 227 228void Model::end_insert_columns() 229{ 230 auto operation = m_operation_stack.take_last(); 231 VERIFY(operation.type == OperationType::Insert); 232 VERIFY(operation.direction == Direction::Column); 233 handle_insert(operation); 234 235 for_each_client([&operation](ModelClient& client) { 236 client.model_did_insert_columns(operation.source_parent, operation.first, operation.last); 237 }); 238} 239 240void Model::end_move_rows() 241{ 242 auto operation = m_operation_stack.take_last(); 243 VERIFY(operation.type == OperationType::Move); 244 VERIFY(operation.direction == Direction::Row); 245 handle_move(operation); 246 247 for_each_client([&operation](ModelClient& client) { 248 client.model_did_move_rows(operation.source_parent, operation.first, operation.last, operation.target_parent, operation.target); 249 }); 250} 251 252void Model::end_move_columns() 253{ 254 auto operation = m_operation_stack.take_last(); 255 VERIFY(operation.type == OperationType::Move); 256 VERIFY(operation.direction == Direction::Column); 257 handle_move(operation); 258 259 for_each_client([&operation](ModelClient& client) { 260 client.model_did_move_columns(operation.source_parent, operation.first, operation.last, operation.target_parent, operation.target); 261 }); 262} 263 264void Model::end_delete_rows() 265{ 266 auto operation = m_operation_stack.take_last(); 267 VERIFY(operation.type == OperationType::Delete); 268 VERIFY(operation.direction == Direction::Row); 269 handle_delete(operation); 270 271 for_each_client([&operation](ModelClient& client) { 272 client.model_did_delete_rows(operation.source_parent, operation.first, operation.last); 273 }); 274} 275 276void Model::end_delete_columns() 277{ 278 auto operation = m_operation_stack.take_last(); 279 VERIFY(operation.type == OperationType::Delete); 280 VERIFY(operation.direction == Direction::Column); 281 handle_delete(operation); 282 283 for_each_client([&operation](ModelClient& client) { 284 client.model_did_delete_columns(operation.source_parent, operation.first, operation.last); 285 }); 286} 287 288void Model::change_persistent_index_list(Vector<ModelIndex> const& old_indices, Vector<ModelIndex> const& new_indices) 289{ 290 VERIFY(old_indices.size() == new_indices.size()); 291 292 for (size_t i = 0; i < old_indices.size(); i++) { 293 auto it = m_persistent_handles.find(old_indices.at(i)); 294 if (it == m_persistent_handles.end()) 295 continue; 296 297 auto handle = move(it->value); 298 m_persistent_handles.remove(it); 299 300 auto new_index = new_indices.at(i); 301 if (new_index.is_valid()) { 302 handle->m_index = new_index; 303 m_persistent_handles.set(new_index, move(handle)); 304 } 305 } 306} 307 308void Model::handle_insert(Operation const& operation) 309{ 310 bool is_row = operation.direction == Direction::Row; 311 Vector<ModelIndex*> to_shift; 312 313 for (auto& entry : m_persistent_handles) { 314 if (entry.key.parent() == operation.source_parent) { 315 if (is_row && entry.key.row() >= operation.first) { 316 to_shift.append(&entry.key); 317 } else if (!is_row && entry.key.column() >= operation.first) { 318 to_shift.append(&entry.key); 319 } 320 } 321 } 322 323 int offset = operation.last - operation.first + 1; 324 325 for (auto current_index : to_shift) { 326 int new_row = is_row ? current_index->row() + offset : current_index->row(); 327 int new_column = is_row ? current_index->column() : current_index->column() + offset; 328 auto new_index = create_index(new_row, new_column, current_index->internal_data()); 329 330 auto it = m_persistent_handles.find(*current_index); 331 auto handle = move(it->value); 332 333 handle->m_index = new_index; 334 335 m_persistent_handles.remove(it); 336 m_persistent_handles.set(move(new_index), move(handle)); 337 } 338} 339 340void Model::handle_delete(Operation const& operation) 341{ 342 bool is_row = operation.direction == Direction::Row; 343 Vector<ModelIndex> deleted_indices = m_deleted_indices_stack.take_last(); 344 Vector<ModelIndex*> to_shift; 345 346 // Get rid of all persistent handles which have been marked for death 347 for (auto& deleted_index : deleted_indices) { 348 m_persistent_handles.remove(deleted_index); 349 } 350 351 for (auto& entry : m_persistent_handles) { 352 if (entry.key.parent() == operation.source_parent) { 353 if (is_row) { 354 if (entry.key.row() > operation.last) { 355 to_shift.append(&entry.key); 356 } 357 } else { 358 if (entry.key.column() > operation.last) { 359 to_shift.append(&entry.key); 360 } 361 } 362 } 363 } 364 365 int offset = operation.last - operation.first + 1; 366 367 for (auto current_index : to_shift) { 368 int new_row = is_row ? current_index->row() - offset : current_index->row(); 369 int new_column = is_row ? current_index->column() : current_index->column() - offset; 370 auto new_index = create_index(new_row, new_column, current_index->internal_data()); 371 372 auto it = m_persistent_handles.find(*current_index); 373 auto handle = move(it->value); 374 375 handle->m_index = new_index; 376 377 m_persistent_handles.remove(it); 378 m_persistent_handles.set(move(new_index), move(handle)); 379 } 380} 381 382void Model::handle_move(Operation const& operation) 383{ 384 bool is_row = operation.direction == Direction::Row; 385 bool move_within = operation.source_parent == operation.target_parent; 386 bool moving_down = operation.target > operation.first; 387 388 if (move_within && operation.first == operation.target) 389 return; 390 391 if (is_row) { 392 VERIFY(operation.target <= row_count(operation.target_parent)); 393 VERIFY(operation.last < row_count(operation.source_parent)); 394 } else { 395 VERIFY(operation.target <= column_count(operation.target_parent)); 396 VERIFY(operation.last < column_count(operation.source_parent)); 397 } 398 399 // NOTE: to_shift_down is used as a generic "to shift" when move_within is true. 400 Vector<ModelIndex*> to_move; // Items to be moved between the source and target 401 Vector<ModelIndex*> to_shift_down; // Items to be shifted down after a move-to 402 Vector<ModelIndex*> to_shift_up; // Items to be shifted up after a move-from 403 404 int count = operation.last - operation.first + 1; 405 // [start, end) 406 int work_area_start = min(operation.first, operation.target); 407 int work_area_end = max(operation.last + 1, operation.target + count); 408 409 for (auto& entry : m_persistent_handles) { 410 int dimension = is_row ? entry.key.row() : entry.key.column(); 411 412 if (move_within) { 413 if (entry.key.parent() == operation.source_parent) { 414 if (dimension >= operation.first && dimension <= operation.last) { 415 to_move.append(&entry.key); 416 } else if (moving_down && dimension > operation.last && dimension < work_area_end) { 417 to_shift_down.append(&entry.key); 418 } else if (!moving_down && dimension >= work_area_start && dimension < operation.first) { 419 to_shift_down.append(&entry.key); 420 } 421 } 422 } else { 423 if (entry.key.parent() == operation.source_parent) { 424 if (dimension >= operation.first && dimension <= operation.last) { 425 to_move.append(&entry.key); 426 } else if (dimension > operation.last) { 427 to_shift_up.append(&entry.key); 428 } 429 } else if (entry.key.parent() == operation.target_parent) { 430 if (dimension >= operation.target) { 431 to_shift_down.append(&entry.key); 432 } 433 } 434 } 435 } 436 437 auto replace_handle = [&](ModelIndex const& current_index, int new_dimension, bool relative) { 438 int new_row = is_row 439 ? (relative 440 ? current_index.row() + new_dimension 441 : new_dimension) 442 : current_index.row(); 443 int new_column = !is_row 444 ? (relative 445 ? current_index.column() + new_dimension 446 : new_dimension) 447 : current_index.column(); 448 auto new_index = index(new_row, new_column, operation.target_parent); 449 450 auto it = m_persistent_handles.find(current_index); 451 auto handle = move(it->value); 452 453 handle->m_index = new_index; 454 455 m_persistent_handles.remove(it); 456 m_persistent_handles.set(move(new_index), move(handle)); 457 }; 458 459 for (auto current_index : to_move) { 460 int dimension = is_row ? current_index->row() : current_index->column(); 461 int target_offset = dimension - operation.first; 462 int new_dimension = operation.target + target_offset; 463 464 replace_handle(*current_index, new_dimension, false); 465 } 466 467 if (move_within) { 468 for (auto current_index : to_shift_down) { 469 int dimension = is_row ? current_index->row() : current_index->column(); 470 int target_offset = moving_down ? dimension - (operation.last + 1) : dimension - work_area_start + count; 471 int new_dimension = work_area_start + target_offset; 472 473 replace_handle(*current_index, new_dimension, false); 474 } 475 } else { 476 for (auto current_index : to_shift_down) { 477 replace_handle(*current_index, count, true); 478 } 479 480 for (auto current_index : to_shift_up) { 481 replace_handle(*current_index, count, true); 482 } 483 } 484} 485 486}