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 adjustSubSortColumn(const int column
)
89 return ((column
>= 0) && (column
< TransferListModel::NB_COLUMNS
))
90 ? column
: TransferListModel::TR_NAME
;
94 TransferListSortModel::TransferListSortModel(QObject
*parent
)
95 : QSortFilterProxyModel
{parent
}
96 , m_subSortColumn
{u
"TransferList/SubSortColumn"_s
, TransferListModel::TR_NAME
, adjustSubSortColumn
}
97 , m_subSortOrder
{u
"TransferList/SubSortOrder"_s
, 0}
99 setSortRole(TransferListModel::UnderlyingDataRole
);
102 void TransferListSortModel::sort(const int column
, const Qt::SortOrder order
)
104 if ((m_lastSortColumn
!= column
) && (m_lastSortColumn
!= -1))
106 m_subSortColumn
= m_lastSortColumn
;
107 m_subSortOrder
= m_lastSortOrder
;
109 m_lastSortColumn
= column
;
110 m_lastSortOrder
= ((order
== Qt::AscendingOrder
) ? 0 : 1);
112 QSortFilterProxyModel::sort(column
, order
);
115 void TransferListSortModel::setStatusFilter(const TorrentFilter::Type filter
)
117 if (m_filter
.setType(filter
))
118 invalidateRowsFilter();
121 void TransferListSortModel::setCategoryFilter(const QString
&category
)
123 if (m_filter
.setCategory(category
))
124 invalidateRowsFilter();
127 void TransferListSortModel::disableCategoryFilter()
129 if (m_filter
.setCategory(TorrentFilter::AnyCategory
))
130 invalidateRowsFilter();
133 void TransferListSortModel::setTagFilter(const Tag
&tag
)
135 if (m_filter
.setTag(tag
))
136 invalidateRowsFilter();
139 void TransferListSortModel::disableTagFilter()
141 if (m_filter
.setTag(TorrentFilter::AnyTag
))
142 invalidateRowsFilter();
145 void TransferListSortModel::setTrackerFilter(const QSet
<BitTorrent::TorrentID
> &torrentIDs
)
147 if (m_filter
.setTorrentIDSet(torrentIDs
))
148 invalidateRowsFilter();
151 void TransferListSortModel::disableTrackerFilter()
153 if (m_filter
.setTorrentIDSet(TorrentFilter::AnyID
))
154 invalidateRowsFilter();
157 int TransferListSortModel::compare(const QModelIndex
&left
, const QModelIndex
&right
) const
159 const int compareColumn
= left
.column();
160 const QVariant leftValue
= left
.data(TransferListModel::UnderlyingDataRole
);
161 const QVariant rightValue
= right
.data(TransferListModel::UnderlyingDataRole
);
163 switch (compareColumn
)
165 case TransferListModel::TR_CATEGORY
:
166 case TransferListModel::TR_DOWNLOAD_PATH
:
167 case TransferListModel::TR_NAME
:
168 case TransferListModel::TR_SAVE_PATH
:
169 case TransferListModel::TR_TRACKER
:
170 return m_naturalCompare(leftValue
.toString(), rightValue
.toString());
172 case TransferListModel::TR_INFOHASH_V1
:
173 return threeWayCompare(leftValue
.value
<SHA1Hash
>(), rightValue
.value
<SHA1Hash
>());
175 case TransferListModel::TR_INFOHASH_V2
:
176 return threeWayCompare(leftValue
.value
<SHA256Hash
>(), rightValue
.value
<SHA256Hash
>());
178 case TransferListModel::TR_TAGS
:
179 return customCompare(leftValue
.value
<TagSet
>(), rightValue
.value
<TagSet
>(), m_naturalCompare
);
181 case TransferListModel::TR_AMOUNT_DOWNLOADED
:
182 case TransferListModel::TR_AMOUNT_DOWNLOADED_SESSION
:
183 case TransferListModel::TR_AMOUNT_LEFT
:
184 case TransferListModel::TR_AMOUNT_UPLOADED
:
185 case TransferListModel::TR_AMOUNT_UPLOADED_SESSION
:
186 case TransferListModel::TR_COMPLETED
:
187 case TransferListModel::TR_ETA
:
188 case TransferListModel::TR_LAST_ACTIVITY
:
189 case TransferListModel::TR_REANNOUNCE
:
190 case TransferListModel::TR_SIZE
:
191 case TransferListModel::TR_TIME_ELAPSED
:
192 case TransferListModel::TR_TOTAL_SIZE
:
193 return customCompare(leftValue
.toLongLong(), rightValue
.toLongLong());
195 case TransferListModel::TR_AVAILABILITY
:
196 case TransferListModel::TR_PROGRESS
:
197 case TransferListModel::TR_RATIO
:
198 case TransferListModel::TR_RATIO_LIMIT
:
199 case TransferListModel::TR_POPULARITY
:
200 return customCompare(leftValue
.toReal(), rightValue
.toReal());
202 case TransferListModel::TR_STATUS
:
203 return threeWayCompare(leftValue
.toInt(), rightValue
.toInt());
205 case TransferListModel::TR_ADD_DATE
:
206 case TransferListModel::TR_SEED_DATE
:
207 case TransferListModel::TR_SEEN_COMPLETE_DATE
:
208 return customCompare(leftValue
.toDateTime(), rightValue
.toDateTime());
210 case TransferListModel::TR_DLLIMIT
:
211 case TransferListModel::TR_DLSPEED
:
212 case TransferListModel::TR_QUEUE_POSITION
:
213 case TransferListModel::TR_UPLIMIT
:
214 case TransferListModel::TR_UPSPEED
:
215 return customCompare(leftValue
.toInt(), rightValue
.toInt());
217 case TransferListModel::TR_PEERS
:
218 case TransferListModel::TR_SEEDS
:
220 // Active peers/seeds take precedence over total peers/seeds
221 const auto activeL
= leftValue
.toInt();
222 const auto activeR
= rightValue
.toInt();
223 if (activeL
!= activeR
)
224 return threeWayCompare(activeL
, activeR
);
226 const auto totalL
= left
.data(TransferListModel::AdditionalUnderlyingDataRole
).toInt();
227 const auto totalR
= right
.data(TransferListModel::AdditionalUnderlyingDataRole
).toInt();
228 return threeWayCompare(totalL
, totalR
);
232 Q_ASSERT_X(false, Q_FUNC_INFO
, "Missing comparison case");
239 bool TransferListSortModel::lessThan(const QModelIndex
&left
, const QModelIndex
&right
) const
241 Q_ASSERT(left
.column() == right
.column());
243 const int result
= compare(left
, right
);
246 const int subResult
= compare(left
.sibling(left
.row(), m_subSortColumn
), right
.sibling(right
.row(), m_subSortColumn
));
247 // Qt inverses lessThan() result when ordered descending.
248 // For sub-sorting we have to do it manually.
249 // When both are ordered descending subResult must be double-inversed, which is the same as no inversion.
250 const bool inverseSubResult
= (m_lastSortOrder
!= m_subSortOrder
); // exactly one is descending
251 return (inverseSubResult
? (subResult
> 0) : (subResult
< 0));
257 bool TransferListSortModel::filterAcceptsRow(const int sourceRow
, const QModelIndex
&sourceParent
) const
259 return matchFilter(sourceRow
, sourceParent
)
260 && QSortFilterProxyModel::filterAcceptsRow(sourceRow
, sourceParent
);
263 bool TransferListSortModel::matchFilter(const int sourceRow
, const QModelIndex
&sourceParent
) const
265 const auto *model
= qobject_cast
<TransferListModel
*>(sourceModel());
266 if (!model
) return false;
268 const BitTorrent::Torrent
*torrent
= model
->torrentHandle(model
->index(sourceRow
, 0, sourceParent
));
269 if (!torrent
) return false;
271 return m_filter
.match(torrent
);