1 /*
2 SPDX-FileCopyrightText: 2019 David Redondo <kde@david-redondo.de>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "slidefiltermodel.h"
8
9 #include "backgroundlistmodel.h"
10 #include "slidemodel.h"
11
12 #include <QRandomGenerator>
13 #include <QFileInfo>
14 #include <QDir>
15
16 #include <algorithm>
17
SlideFilterModel(QObject * parent)18 SlideFilterModel::SlideFilterModel(QObject *parent)
19 : QSortFilterProxyModel{parent}
20 , m_SortingMode{Image::Random}
21 , m_SortingFoldersFirst{false}
22 , m_usedInConfig{false}
23 , m_random(m_randomDevice())
24 {
25 srand(time(nullptr));
26 setSortCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
27 connect(this, &SlideFilterModel::usedInConfigChanged, this, &SlideFilterModel::invalidateFilter);
28 }
29
filterAcceptsRow(int source_row,const QModelIndex & source_parent) const30 bool SlideFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
31 {
32 auto index = sourceModel()->index(source_row, 0, source_parent);
33 return m_usedInConfig || index.data(BackgroundListModel::ToggleRole).toBool();
34 }
35
setSourceModel(QAbstractItemModel * sourceModel)36 void SlideFilterModel::setSourceModel(QAbstractItemModel *sourceModel)
37 {
38 if (this->sourceModel()) {
39 disconnect(this->sourceModel(), nullptr, this, nullptr);
40 }
41 QSortFilterProxyModel::setSourceModel(sourceModel);
42 if (m_SortingMode == Image::Random && !m_usedInConfig) {
43 buildRandomOrder();
44 }
45 if (sourceModel) {
46 connect(sourceModel, &QAbstractItemModel::modelReset, this, &SlideFilterModel::buildRandomOrder);
47 connect(sourceModel, &QAbstractItemModel::rowsInserted, this, [this] {
48 if (m_SortingMode != Image::Random || m_usedInConfig) {
49 return;
50 }
51 const int old_count = m_randomOrder.size();
52 m_randomOrder.resize(this->sourceModel()->rowCount());
53 std::iota(m_randomOrder.begin() + old_count, m_randomOrder.end(), old_count);
54 std::shuffle(m_randomOrder.begin() + old_count, m_randomOrder.end(), m_random);
55 });
56 connect(sourceModel, &QAbstractItemModel::rowsRemoved, this, [this] {
57 if (m_SortingMode != Image::Random || m_usedInConfig) {
58 return;
59 }
60 m_randomOrder.erase(std::remove_if(m_randomOrder.begin(),
61 m_randomOrder.end(),
62 [this](const int v) {
63 return v >= this->sourceModel()->rowCount();
64 }),
65 m_randomOrder.end());
66 });
67 }
68 }
69
lessThan(const QModelIndex & source_left,const QModelIndex & source_right) const70 bool SlideFilterModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
71 {
72 Qt::CaseSensitivity cs = Qt::CaseInsensitive;
73
74 switch (m_SortingMode) {
75 case Image::Random:
76 if (m_usedInConfig) {
77 return source_left.row() < source_right.row();
78 }
79 return m_randomOrder.indexOf(source_left.row()) < m_randomOrder.indexOf(source_right.row());
80 case Image::Alphabetical:
81 if (m_SortingFoldersFirst) {
82 QFileInfo leftFile(getLocalFilePath(source_left));
83 QFileInfo rightFile(getLocalFilePath(source_right));
84 QString leftFilePath = getFilePathWithDir(leftFile);
85 QString rightFilePath = getFilePathWithDir(rightFile);
86
87 if (leftFilePath == rightFilePath) {
88 return QString::compare(leftFile.fileName(), rightFile.fileName(), cs) < 0;
89 } else if (leftFilePath.startsWith(rightFilePath, cs)) {
90 return true;
91 } else if (rightFilePath.startsWith(leftFilePath, cs)) {
92 return false;
93 } else {
94 return QString::compare(leftFilePath, rightFilePath, cs) < 0;
95 }
96 } else {
97 QFileInfo leftFile(getLocalFilePath(source_left));
98 QFileInfo rightFile(getLocalFilePath(source_right));
99 return QString::compare(leftFile.fileName(), rightFile.fileName(), cs) < 0;
100 }
101 case Image::AlphabeticalReversed:
102 if (m_SortingFoldersFirst) {
103 QFileInfo leftFile(getLocalFilePath(source_left));
104 QFileInfo rightFile(getLocalFilePath(source_right));
105 QString leftFilePath = getFilePathWithDir(leftFile);
106 QString rightFilePath = getFilePathWithDir(rightFile);
107
108 if (leftFilePath == rightFilePath) {
109 return QString::compare(leftFile.fileName(), rightFile.fileName(), cs) > 0;
110 } else if (leftFilePath.startsWith(rightFilePath, cs)) {
111 return true;
112 } else if (rightFilePath.startsWith(leftFilePath, cs)) {
113 return false;
114 } else {
115 return QString::compare(leftFilePath, rightFilePath, cs) > 0;
116 }
117 } else {
118 QFileInfo leftFile(getLocalFilePath(source_left));
119 QFileInfo rightFile(getLocalFilePath(source_right));
120 return QString::compare(leftFile.fileName(), rightFile.fileName(), cs) > 0;
121 }
122 case Image::Modified: // oldest first
123 {
124 QFileInfo leftFile(getLocalFilePath(source_left));
125 QFileInfo rightFile(getLocalFilePath(source_right));
126 return leftFile.lastModified() < rightFile.lastModified();
127 }
128 case Image::ModifiedReversed: // newest first
129 {
130 QFileInfo leftFile(getLocalFilePath(source_left));
131 QFileInfo rightFile(getLocalFilePath(source_right));
132 return !(leftFile.lastModified() < rightFile.lastModified());
133 }
134 }
135 Q_UNREACHABLE();
136 }
137
setSortingMode(Image::SlideshowMode slideshowMode,bool slideshowFoldersFirst)138 void SlideFilterModel::setSortingMode(Image::SlideshowMode slideshowMode, bool slideshowFoldersFirst)
139 {
140 m_SortingMode = slideshowMode;
141 m_SortingFoldersFirst = slideshowFoldersFirst;
142 if (m_SortingMode == Image::Random && !m_usedInConfig) {
143 buildRandomOrder();
144 }
145 QSortFilterProxyModel::invalidate();
146 }
147
invalidate()148 void SlideFilterModel::invalidate()
149 {
150 if (m_SortingMode == Image::Random && !m_usedInConfig) {
151 std::shuffle(m_randomOrder.begin(), m_randomOrder.end(), m_random);
152 }
153 QSortFilterProxyModel::invalidate();
154 }
155
invalidateFilter()156 void SlideFilterModel::invalidateFilter()
157 {
158 QSortFilterProxyModel::invalidateFilter();
159 }
160
indexOf(const QString & path)161 int SlideFilterModel::indexOf(const QString &path)
162 {
163 auto sourceIndex = sourceModel()->index(static_cast<SlideModel *>(sourceModel())->indexOf(path), 0);
164 return mapFromSource(sourceIndex).row();
165 }
166
openContainingFolder(int rowIndex)167 void SlideFilterModel::openContainingFolder(int rowIndex)
168 {
169 auto sourceIndex = mapToSource(index(rowIndex, 0));
170 static_cast<SlideModel *>(sourceModel())->openContainingFolder(sourceIndex.row());
171 }
172
buildRandomOrder()173 void SlideFilterModel::buildRandomOrder()
174 {
175 if (sourceModel()) {
176 m_randomOrder.resize(sourceModel()->rowCount());
177 std::iota(m_randomOrder.begin(), m_randomOrder.end(), 0);
178 std::shuffle(m_randomOrder.begin(), m_randomOrder.end(), m_random);
179 }
180 }
181
getLocalFilePath(const QModelIndex & modelIndex) const182 QString SlideFilterModel::getLocalFilePath(const QModelIndex& modelIndex) const
183 {
184 return modelIndex.data(BackgroundListModel::PathRole).toUrl().toLocalFile();
185 }
186
getFilePathWithDir(const QFileInfo & fileInfo) const187 QString SlideFilterModel::getFilePathWithDir(const QFileInfo& fileInfo) const
188 {
189 return fileInfo.canonicalPath().append(QDir::separator());
190 }
191