WebUI: Use Map instead of Mootools Hash in Torrents table
[qBittorrent.git] / src / gui / transferlistsortmodel.cpp
blob782aead742765c01623537c294f2e4589cb6a2cd
1 /*
2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2013 Nick Tiskov <daymansmail@gmail.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * In addition, as a special exception, the copyright holders give permission to
20 * link this program with the OpenSSL project's "OpenSSL" library (or with
21 * modified versions of it that use the same license as the "OpenSSL" library),
22 * and distribute the linked executables. You must obey the GNU General Public
23 * License in all respects for all of the code used other than "OpenSSL". If you
24 * modify file(s), you may extend this exception to your version of the file(s),
25 * but you are not obligated to do so. If you do not wish to do so, delete this
26 * exception statement from your version.
29 #include "transferlistsortmodel.h"
31 #include <type_traits>
33 #include <QDateTime>
35 #include "base/bittorrent/infohash.h"
36 #include "base/bittorrent/torrent.h"
37 #include "transferlistmodel.h"
39 namespace
41 template <typename T>
42 int threeWayCompare(const T &left, const T &right)
44 if (left == right)
45 return 0;
46 return (left < right) ? -1 : 1;
49 int customCompare(const QDateTime &left, const QDateTime &right)
51 const bool isLeftValid = left.isValid();
52 const bool isRightValid = right.isValid();
54 if (isLeftValid == isRightValid)
55 return threeWayCompare(left, right);
56 return isLeftValid ? -1 : 1;
59 int customCompare(const TagSet &left, const TagSet &right, const Utils::Compare::NaturalCompare<Qt::CaseInsensitive> &compare)
61 for (auto leftIter = left.cbegin(), rightIter = right.cbegin();
62 (leftIter != left.cend()) && (rightIter != right.cend());
63 ++leftIter, ++rightIter)
65 const int result = compare(leftIter->toString(), rightIter->toString());
66 if (result != 0)
67 return result;
69 return threeWayCompare(left.size(), right.size());
72 template <typename T>
73 int customCompare(const T left, const T right)
75 static_assert(std::is_arithmetic_v<T>);
77 const bool isLeftValid = (left >= 0);
78 const bool isRightValid = (right >= 0);
80 if (isLeftValid && isRightValid)
81 return threeWayCompare(left, right);
82 if (!isLeftValid && !isRightValid)
83 return 0;
84 return isLeftValid ? -1 : 1;
87 int compareAsBool(const QVariant &left, const QVariant &right)
89 const bool leftValid = left.isValid();
90 const bool rightValid = right.isValid();
91 if (leftValid && rightValid)
92 return threeWayCompare(left.toBool(), right.toBool());
93 if (!leftValid && !rightValid)
94 return 0;
95 return leftValid ? -1 : 1;
98 int adjustSubSortColumn(const int column)
100 return ((column >= 0) && (column < TransferListModel::NB_COLUMNS))
101 ? column : TransferListModel::TR_NAME;
105 TransferListSortModel::TransferListSortModel(QObject *parent)
106 : QSortFilterProxyModel {parent}
107 , m_subSortColumn {u"TransferList/SubSortColumn"_s, TransferListModel::TR_NAME, adjustSubSortColumn}
108 , m_subSortOrder {u"TransferList/SubSortOrder"_s, 0}
110 setSortRole(TransferListModel::UnderlyingDataRole);
113 void TransferListSortModel::sort(const int column, const Qt::SortOrder order)
115 if ((m_lastSortColumn != column) && (m_lastSortColumn != -1))
117 m_subSortColumn = m_lastSortColumn;
118 m_subSortOrder = m_lastSortOrder;
120 m_lastSortColumn = column;
121 m_lastSortOrder = ((order == Qt::AscendingOrder) ? 0 : 1);
123 QSortFilterProxyModel::sort(column, order);
126 void TransferListSortModel::setStatusFilter(const TorrentFilter::Type filter)
128 if (m_filter.setType(filter))
129 invalidateRowsFilter();
132 void TransferListSortModel::setCategoryFilter(const QString &category)
134 if (m_filter.setCategory(category))
135 invalidateRowsFilter();
138 void TransferListSortModel::disableCategoryFilter()
140 if (m_filter.setCategory(TorrentFilter::AnyCategory))
141 invalidateRowsFilter();
144 void TransferListSortModel::setTagFilter(const Tag &tag)
146 if (m_filter.setTag(tag))
147 invalidateRowsFilter();
150 void TransferListSortModel::disableTagFilter()
152 if (m_filter.setTag(TorrentFilter::AnyTag))
153 invalidateRowsFilter();
156 void TransferListSortModel::setTrackerFilter(const QSet<BitTorrent::TorrentID> &torrentIDs)
158 if (m_filter.setTorrentIDSet(torrentIDs))
159 invalidateRowsFilter();
162 void TransferListSortModel::disableTrackerFilter()
164 if (m_filter.setTorrentIDSet(TorrentFilter::AnyID))
165 invalidateRowsFilter();
168 int TransferListSortModel::compare(const QModelIndex &left, const QModelIndex &right) const
170 const int compareColumn = left.column();
171 const QVariant leftValue = left.data(TransferListModel::UnderlyingDataRole);
172 const QVariant rightValue = right.data(TransferListModel::UnderlyingDataRole);
174 switch (compareColumn)
176 case TransferListModel::TR_CATEGORY:
177 case TransferListModel::TR_DOWNLOAD_PATH:
178 case TransferListModel::TR_NAME:
179 case TransferListModel::TR_SAVE_PATH:
180 case TransferListModel::TR_TRACKER:
181 return m_naturalCompare(leftValue.toString(), rightValue.toString());
183 case TransferListModel::TR_INFOHASH_V1:
184 return threeWayCompare(leftValue.value<SHA1Hash>(), rightValue.value<SHA1Hash>());
186 case TransferListModel::TR_INFOHASH_V2:
187 return threeWayCompare(leftValue.value<SHA256Hash>(), rightValue.value<SHA256Hash>());
189 case TransferListModel::TR_TAGS:
190 return customCompare(leftValue.value<TagSet>(), rightValue.value<TagSet>(), m_naturalCompare);
192 case TransferListModel::TR_AMOUNT_DOWNLOADED:
193 case TransferListModel::TR_AMOUNT_DOWNLOADED_SESSION:
194 case TransferListModel::TR_AMOUNT_LEFT:
195 case TransferListModel::TR_AMOUNT_UPLOADED:
196 case TransferListModel::TR_AMOUNT_UPLOADED_SESSION:
197 case TransferListModel::TR_COMPLETED:
198 case TransferListModel::TR_ETA:
199 case TransferListModel::TR_LAST_ACTIVITY:
200 case TransferListModel::TR_REANNOUNCE:
201 case TransferListModel::TR_SIZE:
202 case TransferListModel::TR_TIME_ELAPSED:
203 case TransferListModel::TR_TOTAL_SIZE:
204 return customCompare(leftValue.toLongLong(), rightValue.toLongLong());
206 case TransferListModel::TR_AVAILABILITY:
207 case TransferListModel::TR_PROGRESS:
208 case TransferListModel::TR_RATIO:
209 case TransferListModel::TR_RATIO_LIMIT:
210 case TransferListModel::TR_POPULARITY:
211 return customCompare(leftValue.toReal(), rightValue.toReal());
213 case TransferListModel::TR_STATUS:
214 return threeWayCompare(leftValue.toInt(), rightValue.toInt());
216 case TransferListModel::TR_ADD_DATE:
217 case TransferListModel::TR_SEED_DATE:
218 case TransferListModel::TR_SEEN_COMPLETE_DATE:
219 return customCompare(leftValue.toDateTime(), rightValue.toDateTime());
221 case TransferListModel::TR_DLLIMIT:
222 case TransferListModel::TR_DLSPEED:
223 case TransferListModel::TR_QUEUE_POSITION:
224 case TransferListModel::TR_UPLIMIT:
225 case TransferListModel::TR_UPSPEED:
226 return customCompare(leftValue.toInt(), rightValue.toInt());
228 case TransferListModel::TR_PRIVATE:
229 return compareAsBool(leftValue, rightValue);
231 case TransferListModel::TR_PEERS:
232 case TransferListModel::TR_SEEDS:
234 // Active peers/seeds take precedence over total peers/seeds
235 const auto activeL = leftValue.toInt();
236 const auto activeR = rightValue.toInt();
237 if (activeL != activeR)
238 return threeWayCompare(activeL, activeR);
240 const auto totalL = left.data(TransferListModel::AdditionalUnderlyingDataRole).toInt();
241 const auto totalR = right.data(TransferListModel::AdditionalUnderlyingDataRole).toInt();
242 return threeWayCompare(totalL, totalR);
245 default:
246 Q_ASSERT_X(false, Q_FUNC_INFO, "Missing comparison case");
247 break;
250 return 0;
253 bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
255 Q_ASSERT(left.column() == right.column());
257 const int result = compare(left, right);
258 if (result == 0)
260 const int subResult = compare(left.sibling(left.row(), m_subSortColumn), right.sibling(right.row(), m_subSortColumn));
261 // Qt inverses lessThan() result when ordered descending.
262 // For sub-sorting we have to do it manually.
263 // When both are ordered descending subResult must be double-inversed, which is the same as no inversion.
264 const bool inverseSubResult = (m_lastSortOrder != m_subSortOrder); // exactly one is descending
265 return (inverseSubResult ? (subResult > 0) : (subResult < 0));
268 return result < 0;
271 bool TransferListSortModel::filterAcceptsRow(const int sourceRow, const QModelIndex &sourceParent) const
273 return matchFilter(sourceRow, sourceParent)
274 && QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
277 bool TransferListSortModel::matchFilter(const int sourceRow, const QModelIndex &sourceParent) const
279 const auto *model = qobject_cast<TransferListModel *>(sourceModel());
280 if (!model) return false;
282 const BitTorrent::Torrent *torrent = model->torrentHandle(model->index(sourceRow, 0, sourceParent));
283 if (!torrent) return false;
285 return m_filter.match(torrent);