2023-11-01 21:11:11 +00:00
|
|
|
// This file Copyright © Mnemosyne LLC.
|
2022-02-07 16:25:02 +00:00
|
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
2022-01-20 18:27:56 +00:00
|
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
|
|
// License text can be found in the licenses/ folder.
|
2009-04-09 18:55:47 +00:00
|
|
|
|
2022-07-26 02:45:54 +00:00
|
|
|
#include <array>
|
2019-11-07 01:13:41 +00:00
|
|
|
#include <optional>
|
2009-04-09 18:55:47 +00:00
|
|
|
|
2023-07-16 02:55:44 +00:00
|
|
|
#include "libtransmission/utils.h"
|
|
|
|
|
2015-06-10 21:27:11 +00:00
|
|
|
#include "Filters.h"
|
|
|
|
#include "Prefs.h"
|
|
|
|
#include "Torrent.h"
|
|
|
|
#include "TorrentFilter.h"
|
|
|
|
#include "TorrentModel.h"
|
|
|
|
#include "Utils.h"
|
2009-04-09 18:55:47 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
TorrentFilter::TorrentFilter(Prefs const& prefs)
|
2023-11-21 15:02:03 +00:00
|
|
|
: prefs_{ prefs }
|
2009-04-09 18:55:47 +00:00
|
|
|
{
|
2020-05-27 21:53:12 +00:00
|
|
|
connect(&prefs_, &Prefs::changed, this, &TorrentFilter::onPrefChanged);
|
|
|
|
connect(&refilter_timer_, &QTimer::timeout, this, &TorrentFilter::refilter);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
setDynamicSortFilter(true);
|
|
|
|
|
2019-11-07 01:13:41 +00:00
|
|
|
refilter();
|
2009-04-09 18:55:47 +00:00
|
|
|
}
|
|
|
|
|
2019-11-07 01:13:41 +00:00
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
|
|
|
void TorrentFilter::onPrefChanged(int key)
|
2009-04-09 18:55:47 +00:00
|
|
|
{
|
2019-11-07 01:13:41 +00:00
|
|
|
// For refiltering nearly immediately. Used to debounce batched prefs changes.
|
2020-06-05 19:02:11 +00:00
|
|
|
static int const FastMSec = 50;
|
2019-11-07 01:13:41 +00:00
|
|
|
|
|
|
|
// For waiting a little longer. Useful when user is typing the filter text.
|
2020-06-05 19:02:11 +00:00
|
|
|
static int const SlowMSec = 500;
|
2019-11-07 01:13:41 +00:00
|
|
|
|
|
|
|
std::optional<int> msec;
|
2017-04-19 12:04:45 +00:00
|
|
|
switch (key)
|
2009-04-09 18:55:47 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
case Prefs::FILTER_TEXT:
|
2019-11-07 01:13:41 +00:00
|
|
|
// special case for isEmpty: user probably hit the 'clear' button
|
2020-06-05 19:02:11 +00:00
|
|
|
msec = prefs_.getString(key).isEmpty() ? FastMSec : SlowMSec;
|
2019-11-07 01:13:41 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case Prefs::FILTER_MODE:
|
|
|
|
case Prefs::FILTER_TRACKERS:
|
|
|
|
case Prefs::SORT_MODE:
|
|
|
|
case Prefs::SORT_REVERSED:
|
2020-06-05 19:02:11 +00:00
|
|
|
msec = FastMSec;
|
2013-09-14 22:45:04 +00:00
|
|
|
break;
|
2009-04-09 18:55:47 +00:00
|
|
|
}
|
2019-11-07 01:13:41 +00:00
|
|
|
|
|
|
|
// if this pref change affects filtering, ensure that a refilter is queued
|
2020-05-27 21:53:12 +00:00
|
|
|
if (msec && !refilter_timer_.isActive())
|
2019-11-07 01:13:41 +00:00
|
|
|
{
|
2020-05-27 21:53:12 +00:00
|
|
|
refilter_timer_.setSingleShot(true);
|
|
|
|
refilter_timer_.start(*msec);
|
2019-11-07 01:13:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentFilter::refilter()
|
|
|
|
{
|
|
|
|
invalidate();
|
2020-05-27 21:53:12 +00:00
|
|
|
sort(0, prefs_.getBool(Prefs::SORT_REVERSED) ? Qt::AscendingOrder : Qt::DescendingOrder);
|
2009-04-09 18:55:47 +00:00
|
|
|
}
|
|
|
|
|
2009-04-11 18:25:12 +00:00
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
bool TorrentFilter::lessThan(QModelIndex const& left, QModelIndex const& right) const
|
2009-04-09 18:55:47 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
int val = 0;
|
2020-05-20 01:32:51 +00:00
|
|
|
auto const* a = sourceModel()->data(left, TorrentModel::TorrentRole).value<Torrent const*>();
|
|
|
|
auto const* b = sourceModel()->data(right, TorrentModel::TorrentRole).value<Torrent const*>();
|
2009-04-09 18:55:47 +00:00
|
|
|
|
2020-05-27 21:53:12 +00:00
|
|
|
switch (prefs_.get<SortMode>(Prefs::SORT_MODE).mode())
|
2009-04-09 18:55:47 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
case SortMode::SORT_BY_QUEUE:
|
2017-04-30 16:25:26 +00:00
|
|
|
if (val == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-07-16 02:55:44 +00:00
|
|
|
val = -tr_compare_3way(a->queuePosition(), b->queuePosition());
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2013-09-14 22:45:04 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case SortMode::SORT_BY_SIZE:
|
2017-04-30 16:25:26 +00:00
|
|
|
if (val == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-07-16 02:55:44 +00:00
|
|
|
val = tr_compare_3way(a->sizeWhenDone(), b->sizeWhenDone());
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2013-09-14 22:45:04 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case SortMode::SORT_BY_AGE:
|
2019-11-06 23:31:41 +00:00
|
|
|
if (val == 0)
|
|
|
|
{
|
2023-07-16 02:55:44 +00:00
|
|
|
val = tr_compare_3way(a->dateAdded(), b->dateAdded());
|
2019-11-06 23:31:41 +00:00
|
|
|
}
|
|
|
|
|
2013-09-14 22:45:04 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case SortMode::SORT_BY_ID:
|
2017-04-30 16:25:26 +00:00
|
|
|
if (val == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-07-16 02:55:44 +00:00
|
|
|
val = tr_compare_3way(a->id(), b->id());
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2013-09-14 22:45:04 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case SortMode::SORT_BY_ACTIVITY:
|
2017-04-30 16:25:26 +00:00
|
|
|
if (val == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-07-16 02:55:44 +00:00
|
|
|
val = tr_compare_3way(a->downloadSpeed() + a->uploadSpeed(), b->downloadSpeed() + b->uploadSpeed());
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (val == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-07-16 02:55:44 +00:00
|
|
|
val = tr_compare_3way(
|
2021-08-15 09:41:48 +00:00
|
|
|
a->peersWeAreUploadingTo() + a->webseedsWeAreDownloadingFrom(),
|
2017-04-19 12:04:45 +00:00
|
|
|
b->peersWeAreUploadingTo() + b->webseedsWeAreDownloadingFrom());
|
|
|
|
}
|
2013-09-14 22:45:04 +00:00
|
|
|
|
2020-08-15 15:42:51 +00:00
|
|
|
[[fallthrough]];
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
case SortMode::SORT_BY_STATE:
|
2017-04-30 16:25:26 +00:00
|
|
|
if (val == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-07-16 02:55:44 +00:00
|
|
|
val = -tr_compare_3way(a->isPaused(), b->isPaused());
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (val == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-07-16 02:55:44 +00:00
|
|
|
val = tr_compare_3way(a->getActivity(), b->getActivity());
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (val == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-07-16 02:55:44 +00:00
|
|
|
val = -tr_compare_3way(a->queuePosition(), b->queuePosition());
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (val == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-07-16 02:55:44 +00:00
|
|
|
val = tr_compare_3way(a->hasError(), b->hasError());
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 15:42:51 +00:00
|
|
|
[[fallthrough]];
|
2013-09-14 22:45:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case SortMode::SORT_BY_PROGRESS:
|
2017-04-20 21:43:35 +00:00
|
|
|
if (val == 0)
|
2017-03-30 02:22:01 +00:00
|
|
|
{
|
2023-07-16 02:55:44 +00:00
|
|
|
val = tr_compare_3way(a->metadataPercentDone(), b->metadataPercentDone());
|
2017-03-30 02:22:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (val == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-07-16 02:55:44 +00:00
|
|
|
val = tr_compare_3way(a->percentComplete(), b->percentComplete());
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (val == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-09-16 17:38:32 +00:00
|
|
|
val = a->compareSeedProgress(*b);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (val == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-07-16 02:55:44 +00:00
|
|
|
val = -tr_compare_3way(a->queuePosition(), b->queuePosition());
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2013-09-14 22:45:04 +00:00
|
|
|
|
2020-08-15 15:42:51 +00:00
|
|
|
[[fallthrough]];
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
case SortMode::SORT_BY_RATIO:
|
2017-04-30 16:25:26 +00:00
|
|
|
if (val == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
val = a->compareRatio(*b);
|
|
|
|
}
|
|
|
|
|
2013-09-14 22:45:04 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case SortMode::SORT_BY_ETA:
|
2017-04-30 16:25:26 +00:00
|
|
|
if (val == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
val = a->compareETA(*b);
|
|
|
|
}
|
|
|
|
|
2013-09-14 22:45:04 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
default:
|
2013-09-14 22:45:04 +00:00
|
|
|
break;
|
2009-04-09 18:55:47 +00:00
|
|
|
}
|
2013-09-14 22:45:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (val == 0)
|
|
|
|
{
|
|
|
|
val = -a->name().compare(b->name(), Qt::CaseInsensitive);
|
|
|
|
}
|
2013-09-14 22:45:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (val == 0)
|
|
|
|
{
|
2023-07-16 02:55:44 +00:00
|
|
|
val = tr_compare_3way(a->hash(), b->hash());
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2013-09-14 22:45:04 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return val < 0;
|
2009-04-09 18:55:47 +00:00
|
|
|
}
|
|
|
|
|
2009-04-11 18:25:12 +00:00
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2020-05-27 21:53:12 +00:00
|
|
|
bool TorrentFilter::filterAcceptsRow(int source_row, QModelIndex const& source_parent) const
|
2010-07-30 22:24:48 +00:00
|
|
|
{
|
2022-07-27 14:03:13 +00:00
|
|
|
QModelIndex const child_index = sourceModel()->index(source_row, 0, source_parent);
|
2020-06-23 23:54:08 +00:00
|
|
|
auto const& tor = *child_index.model()->data(child_index, TorrentModel::TorrentRole).value<Torrent const*>();
|
2017-04-19 12:04:45 +00:00
|
|
|
bool accepts = true;
|
2010-07-30 22:24:48 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (accepts)
|
2013-09-14 22:45:04 +00:00
|
|
|
{
|
2020-05-27 21:53:12 +00:00
|
|
|
auto const m = prefs_.get<FilterMode>(Prefs::FILTER_MODE);
|
2020-06-23 23:54:08 +00:00
|
|
|
accepts = m.test(tor);
|
2010-07-30 22:24:48 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (accepts)
|
2013-09-14 22:45:04 +00:00
|
|
|
{
|
2020-06-24 18:28:54 +00:00
|
|
|
auto const display_name = prefs_.getString(Prefs::FILTER_TRACKERS);
|
2022-02-12 17:30:27 +00:00
|
|
|
accepts = display_name.isEmpty() || tor.includesTracker(display_name.toLower());
|
2009-04-11 18:25:12 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (accepts)
|
2013-09-14 22:45:04 +00:00
|
|
|
{
|
2020-06-23 23:54:08 +00:00
|
|
|
auto const text = prefs_.getString(Prefs::FILTER_TEXT);
|
2021-08-27 17:58:47 +00:00
|
|
|
accepts = text.isEmpty() || tor.name().contains(text, Qt::CaseInsensitive) ||
|
|
|
|
tor.hash().toString().contains(text, Qt::CaseInsensitive);
|
2010-07-30 22:24:48 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return accepts;
|
2009-04-11 18:25:12 +00:00
|
|
|
}
|
|
|
|
|
2020-06-05 19:02:11 +00:00
|
|
|
std::array<int, FilterMode::NUM_MODES> TorrentFilter::countTorrentsPerMode() const
|
2010-07-30 22:24:48 +00:00
|
|
|
{
|
2023-12-17 18:49:39 +00:00
|
|
|
auto* const torrent_model = dynamic_cast<TorrentModel*>(sourceModel());
|
|
|
|
if (torrent_model == nullptr)
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
auto torrent_counts = std::array<int, FilterMode::NUM_MODES>{};
|
2012-12-30 01:28:28 +00:00
|
|
|
|
2023-12-17 18:49:39 +00:00
|
|
|
for (auto const& tor : torrent_model->torrents())
|
2015-01-02 11:15:31 +00:00
|
|
|
{
|
2019-11-12 01:37:05 +00:00
|
|
|
for (int mode = 0; mode < FilterMode::NUM_MODES; ++mode)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2020-06-23 23:54:08 +00:00
|
|
|
if (FilterMode::test(*tor, mode))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2020-06-05 19:02:11 +00:00
|
|
|
++torrent_counts[mode];
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
}
|
2010-07-30 22:24:48 +00:00
|
|
|
}
|
2020-06-05 19:02:11 +00:00
|
|
|
|
|
|
|
return torrent_counts;
|
2010-07-30 22:24:48 +00:00
|
|
|
}
|