Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <AK/QuickSort.h>
28#include <LibGUI/AbstractView.h>
29#include <LibGUI/SortingProxyModel.h>
30#include <stdio.h>
31#include <stdlib.h>
32
33namespace GUI {
34
35SortingProxyModel::SortingProxyModel(NonnullRefPtr<Model>&& target)
36 : m_target(move(target))
37 , m_key_column(-1)
38{
39 m_target->on_update = [this] {
40 resort();
41 };
42}
43
44SortingProxyModel::~SortingProxyModel()
45{
46}
47
48int SortingProxyModel::row_count(const ModelIndex& index) const
49{
50 return target().row_count(index);
51}
52
53int SortingProxyModel::column_count(const ModelIndex& index) const
54{
55 return target().column_count(index);
56}
57
58ModelIndex SortingProxyModel::map_to_target(const ModelIndex& index) const
59{
60 if (!index.is_valid())
61 return {};
62 if (static_cast<size_t>(index.row()) >= m_row_mappings.size() || index.column() >= column_count())
63 return {};
64 return target().index(m_row_mappings[index.row()], index.column());
65}
66
67String SortingProxyModel::row_name(int index) const
68{
69 return target().row_name(index);
70}
71
72String SortingProxyModel::column_name(int index) const
73{
74 return target().column_name(index);
75}
76
77Model::ColumnMetadata SortingProxyModel::column_metadata(int index) const
78{
79 return target().column_metadata(index);
80}
81
82Variant SortingProxyModel::data(const ModelIndex& index, Role role) const
83{
84 auto target_index = map_to_target(index);
85 if (!target_index.is_valid()) {
86 dbg() << "BUG! SortingProxyModel: Unable to convert " << index << " to target";
87 return {};
88 }
89 return target().data(map_to_target(index), role);
90}
91
92void SortingProxyModel::update()
93{
94 target().update();
95}
96
97StringView SortingProxyModel::drag_data_type() const
98{
99 return target().drag_data_type();
100}
101
102void SortingProxyModel::set_key_column_and_sort_order(int column, SortOrder sort_order)
103{
104 if (column == m_key_column && sort_order == m_sort_order)
105 return;
106
107 ASSERT(column >= 0 && column < column_count());
108 m_key_column = column;
109 m_sort_order = sort_order;
110 resort();
111}
112
113void SortingProxyModel::resort()
114{
115 auto old_row_mappings = m_row_mappings;
116 int row_count = target().row_count();
117 m_row_mappings.resize(row_count);
118 for (int i = 0; i < row_count; ++i)
119 m_row_mappings[i] = i;
120 if (m_key_column == -1) {
121 did_update();
122 return;
123 }
124 quick_sort(m_row_mappings.begin(), m_row_mappings.end(), [&](auto row1, auto row2) -> bool {
125 auto data1 = target().data(target().index(row1, m_key_column), Model::Role::Sort);
126 auto data2 = target().data(target().index(row2, m_key_column), Model::Role::Sort);
127 if (data1 == data2)
128 return 0;
129 bool is_less_than;
130 if (data1.is_string() && data2.is_string() && !m_sorting_case_sensitive)
131 is_less_than = data1.as_string().to_lowercase() < data2.as_string().to_lowercase();
132 else
133 is_less_than = data1 < data2;
134 return m_sort_order == SortOrder::Ascending ? is_less_than : !is_less_than;
135 });
136 did_update();
137 for_each_view([&](AbstractView& view) {
138 auto& selection = view.selection();
139 Vector<ModelIndex> selected_indexes_in_target;
140 selection.for_each_index([&](const ModelIndex& index) {
141 selected_indexes_in_target.append(target().index(old_row_mappings[index.row()], index.column()));
142 });
143
144 selection.clear();
145 for (auto& index : selected_indexes_in_target) {
146 for (size_t i = 0; i < m_row_mappings.size(); ++i) {
147 if (m_row_mappings[i] == index.row()) {
148 selection.add(this->index(i, index.column()));
149 continue;
150 }
151 }
152 }
153 });
154}
155
156}