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>
35 #include "base/bittorrent/infohash.h"
36 #include "base/bittorrent/torrent.h"
37 #include "transferlistmodel.h"
42 int threeWayCompare(const T
&left
, const T
&right
)
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());
69 return threeWayCompare(left
.size(), right
.size());
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
)
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
)
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
);
246 Q_ASSERT_X(false, Q_FUNC_INFO
, "Missing comparison case");
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
);
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));
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
);