Serenity Operating System
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}