Merge branch 'master' into patch-1

This commit is contained in:
Charles Kerr 2021-12-09 11:17:44 -06:00 committed by GitHub
commit ef7ff98f03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 521 additions and 573 deletions

View File

@ -97,6 +97,9 @@
<Component Id="dll.qt.network">
<File DiskId="1" KeyPath="yes" Name="Qt$(var.QtMajorVer)Network.dll" />
</Component>
<Component Id="dll.qt.svg">
<File DiskId="1" KeyPath="yes" Name="Qt$(var.QtMajorVer)Svg.dll" />
</Component>
<Component Id="dll.qt.widgets">
<File DiskId="1" KeyPath="yes" Name="Qt$(var.QtMajorVer)Widgets.dll" />
</Component>
@ -109,7 +112,6 @@
</DirectoryRef>
<DirectoryRef Id="INSTALLDIR" FileSource="$(var.QtDir)\plugins">
<?if $(var.QtMajorVer) = 6 ?>
<Directory Id="QTIMAGEFORMATSDIR" Name="imageformats">
<Component Id="dll.qt.plugins.imageformats.gif">
<File DiskId="1" KeyPath="yes" Name="qgif.dll" />
@ -120,8 +122,10 @@
<Component Id="dll.qt.plugins.imageformats.jpeg">
<File DiskId="1" KeyPath="yes" Name="qjpeg.dll" />
</Component>
<Component Id="dll.qt.plugins.imageformats.svg">
<File DiskId="1" KeyPath="yes" Name="qsvg.dll" />
</Component>
</Directory>
<?endif ?>
<Directory Id="QTPLATFORMDIR" Name="platforms">
<Component Id="dll.qt.plugins.platforms.windows">
@ -161,15 +165,15 @@
<ComponentRef Id="dll.qt.dbus" />
<ComponentRef Id="dll.qt.gui" />
<ComponentRef Id="dll.qt.network" />
<ComponentRef Id="dll.qt.svg" />
<ComponentRef Id="dll.qt.widgets" />
<?if $(var.QtMajorVer) = 5 ?>
<ComponentRef Id="dll.qt.winextras" />
<?endif ?>
<?if $(var.QtMajorVer) = 6 ?>
<ComponentRef Id="dll.qt.plugins.imageformats.gif" />
<ComponentRef Id="dll.qt.plugins.imageformats.ico" />
<ComponentRef Id="dll.qt.plugins.imageformats.jpeg" />
<?endif ?>
<ComponentRef Id="dll.qt.plugins.imageformats.svg" />
<ComponentRef Id="dll.qt.plugins.platforms.windows" />
<ComponentRef Id="dll.qt.plugins.styles.windowsvista" />
<?if $(var.QtMajorVer) = 6 ?>

View File

@ -1403,7 +1403,7 @@ void DetailsDialog::Impl::refreshPeerList(std::vector<tr_torrent*> const& torren
void DetailsDialog::Impl::refreshWebseedList(std::vector<tr_torrent*> const& torrents)
{
int total = 0;
auto has_any_webseeds = bool{ false };
auto& hash = webseed_hash_;
auto& store = webseed_store_;
@ -1416,13 +1416,11 @@ void DetailsDialog::Impl::refreshWebseedList(std::vector<tr_torrent*> const& tor
/* step 2: add any new webseeds */
for (auto const* const tor : torrents)
{
auto const* inf = tr_torrentInfo(tor);
total += inf->webseedCount;
for (unsigned int j = 0; j < inf->webseedCount; ++j)
for (size_t j = 0, n = tr_torrentWebseedCount(tor); j < n; ++j)
{
char const* url = inf->webseeds[j];
has_any_webseeds = true;
auto const* const url = tr_torrentWebseed(tor, j).url;
auto const key = gtr_sprintf("%d.%s", tr_torrentId(tor), url);
if (hash.find(key) == hash.end())
@ -1438,27 +1436,23 @@ void DetailsDialog::Impl::refreshWebseedList(std::vector<tr_torrent*> const& tor
/* step 3: update the webseeds */
for (auto const* const tor : torrents)
{
auto const* inf = tr_torrentInfo(tor);
double* speeds_KBps = tr_torrentWebSpeeds_KBps(tor);
for (unsigned int j = 0; j < inf->webseedCount; ++j)
for (size_t j = 0, n = tr_torrentWebseedCount(tor); j < n; ++j)
{
char const* const url = inf->webseeds[j];
auto const key = gtr_sprintf("%d.%s", tr_torrentId(tor), url);
auto const webseed = tr_torrentWebseed(tor, j);
auto const key = gtr_sprintf("%d.%s", tr_torrentId(tor), webseed.url);
auto const iter = store->get_iter(hash.at(key).get_path());
char buf[128] = { 0 };
if (speeds_KBps[j] > 0)
auto const KBps = double(webseed.download_bytes_per_second) / speed_K;
auto buf = std::array<char, 128>{};
if (webseed.is_downloading)
{
tr_formatter_speed_KBps(buf, speeds_KBps[j], sizeof(buf));
tr_formatter_speed_KBps(std::data(buf), KBps, std::size(buf));
}
(*iter)[webseed_cols.download_rate_double] = speeds_KBps[j];
(*iter)[webseed_cols.download_rate_string] = buf;
(*iter)[webseed_cols.download_rate_double] = KBps;
(*iter)[webseed_cols.download_rate_string] = std::data(buf);
(*iter)[webseed_cols.was_updated] = true;
}
tr_free(speeds_KBps);
}
/* step 4: remove webseeds that have disappeared */
@ -1481,7 +1475,7 @@ void DetailsDialog::Impl::refreshWebseedList(std::vector<tr_torrent*> const& tor
/* most of the time there are no webseeds...
don't waste space showing an empty list */
webseed_view_->set_visible(total > 0);
webseed_view_->set_visible(has_any_webseeds);
}
void DetailsDialog::Impl::refreshPeers(std::vector<tr_torrent*> const& torrents)
@ -1853,24 +1847,24 @@ Glib::ustring tr_strltime_rounded(time_t t)
return tr_strltime(t);
}
void appendAnnounceInfo(tr_tracker_stat const* const st, time_t const now, Gtk::TextDirection direction, std::ostream& gstr)
void appendAnnounceInfo(tr_tracker_view const& tracker, time_t const now, Gtk::TextDirection direction, std::ostream& gstr)
{
if (st->hasAnnounced && st->announceState != TR_TRACKER_INACTIVE)
if (tracker.hasAnnounced && tracker.announceState != TR_TRACKER_INACTIVE)
{
gstr << '\n';
gstr << text_dir_mark[direction];
auto const timebuf = tr_strltime_rounded(now - st->lastAnnounceTime);
auto const timebuf = tr_strltime_rounded(now - tracker.lastAnnounceTime);
if (st->lastAnnounceSucceeded)
if (tracker.lastAnnounceSucceeded)
{
gstr << gtr_sprintf(
_("Got a list of %1$s%2$'d peers%3$s %4$s ago"),
success_markup_begin,
st->lastAnnouncePeerCount,
tracker.lastAnnouncePeerCount,
success_markup_end,
timebuf);
}
else if (st->lastAnnounceTimedOut)
else if (tracker.lastAnnounceTimedOut)
{
gstr << gtr_sprintf(
_("Peer list request %1$stimed out%2$s %3$s ago; will retry"),
@ -1883,13 +1877,13 @@ void appendAnnounceInfo(tr_tracker_stat const* const st, time_t const now, Gtk::
gstr << gtr_sprintf(
_("Got an error %1$s\"%2$s\"%3$s %4$s ago"),
err_markup_begin,
st->lastAnnounceResult,
tracker.lastAnnounceResult,
err_markup_end,
timebuf);
}
}
switch (st->announceState)
switch (tracker.announceState)
{
case TR_TRACKER_INACTIVE:
gstr << '\n';
@ -1900,7 +1894,7 @@ void appendAnnounceInfo(tr_tracker_stat const* const st, time_t const now, Gtk::
case TR_TRACKER_WAITING:
gstr << '\n';
gstr << text_dir_mark[direction];
gstr << gtr_sprintf(_("Asking for more peers in %s"), tr_strltime_rounded(st->nextAnnounceTime - now));
gstr << gtr_sprintf(_("Asking for more peers in %s"), tr_strltime_rounded(tracker.nextAnnounceTime - now));
break;
case TR_TRACKER_QUEUED:
@ -1914,26 +1908,26 @@ void appendAnnounceInfo(tr_tracker_stat const* const st, time_t const now, Gtk::
gstr << text_dir_mark[direction];
gstr << gtr_sprintf(
_("Asking for more peers now… <small>%s</small>"),
tr_strltime_rounded(now - st->lastAnnounceStartTime));
tr_strltime_rounded(now - tracker.lastAnnounceStartTime));
break;
}
}
void appendScrapeInfo(tr_tracker_stat const* const st, time_t const now, Gtk::TextDirection direction, std::ostream& gstr)
void appendScrapeInfo(tr_tracker_view const& tracker, time_t const now, Gtk::TextDirection direction, std::ostream& gstr)
{
if (st->hasScraped)
if (tracker.hasScraped)
{
gstr << '\n';
gstr << text_dir_mark[direction];
auto const timebuf = tr_strltime_rounded(now - st->lastScrapeTime);
auto const timebuf = tr_strltime_rounded(now - tracker.lastScrapeTime);
if (st->lastScrapeSucceeded)
if (tracker.lastScrapeSucceeded)
{
gstr << gtr_sprintf(
_("Tracker had %s%'d seeders and %'d leechers%s %s ago"),
success_markup_begin,
st->seederCount,
st->leecherCount,
tracker.seederCount,
tracker.leecherCount,
success_markup_end,
timebuf);
}
@ -1942,13 +1936,13 @@ void appendScrapeInfo(tr_tracker_stat const* const st, time_t const now, Gtk::Te
gstr << gtr_sprintf(
_("Got a scrape error \"%s%s%s\" %s ago"),
err_markup_begin,
st->lastScrapeResult,
tracker.lastScrapeResult,
err_markup_end,
timebuf);
}
}
switch (st->scrapeState)
switch (tracker.scrapeState)
{
case TR_TRACKER_INACTIVE:
break;
@ -1956,7 +1950,7 @@ void appendScrapeInfo(tr_tracker_stat const* const st, time_t const now, Gtk::Te
case TR_TRACKER_WAITING:
gstr << '\n';
gstr << text_dir_mark[direction];
gstr << gtr_sprintf(_("Asking for peer counts in %s"), tr_strltime_rounded(st->nextScrapeTime - now));
gstr << gtr_sprintf(_("Asking for peer counts in %s"), tr_strltime_rounded(tracker.nextScrapeTime - now));
break;
case TR_TRACKER_QUEUED:
@ -1970,7 +1964,7 @@ void appendScrapeInfo(tr_tracker_stat const* const st, time_t const now, Gtk::Te
gstr << text_dir_mark[direction];
gstr << gtr_sprintf(
_("Asking for peer counts now… <small>%s</small>"),
tr_strltime_rounded(now - st->lastScrapeStartTime));
tr_strltime_rounded(now - tracker.lastScrapeStartTime));
break;
}
}
@ -1978,25 +1972,25 @@ void appendScrapeInfo(tr_tracker_stat const* const st, time_t const now, Gtk::Te
void buildTrackerSummary(
std::ostream& gstr,
std::string const& key,
tr_tracker_stat const* st,
tr_tracker_view const& tracker,
bool showScrape,
Gtk::TextDirection direction)
{
// hostname
gstr << text_dir_mark[direction];
gstr << (st->isBackup ? "<i>" : "<b>");
gstr << Glib::Markup::escape_text(!key.empty() ? gtr_sprintf("%s - %s", st->host, key) : st->host);
gstr << (st->isBackup ? "</i>" : "</b>");
gstr << (tracker.isBackup ? "<i>" : "<b>");
gstr << Glib::Markup::escape_text(!key.empty() ? gtr_sprintf("%s - %s", tracker.host, key) : tracker.host);
gstr << (tracker.isBackup ? "</i>" : "</b>");
if (!st->isBackup)
if (!tracker.isBackup)
{
time_t const now = time(nullptr);
appendAnnounceInfo(st, now, direction, gstr);
appendAnnounceInfo(tracker, now, direction, gstr);
if (showScrape)
{
appendScrapeInfo(st, now, direction, gstr);
appendScrapeInfo(tracker, now, direction, gstr);
}
}
}
@ -2098,16 +2092,13 @@ void DetailsDialog::Impl::refreshTracker(std::vector<tr_torrent*> const& torrent
bool const showScrape = scrape_check_->get_active();
/* step 1: get all the trackers */
std::vector<int> statCount;
std::vector<tr_tracker_stat*> stats;
statCount.reserve(torrents.size());
stats.reserve(torrents.size());
for (auto const* torrent : torrents)
auto trackers = std::multimap<tr_torrent const*, tr_tracker_view>{};
for (auto const* tor : torrents)
{
int count = 0;
stats.push_back(tr_torrentTrackers(torrent, &count));
statCount.push_back(count);
for (size_t i = 0, n = tr_torrentTrackerCount(tor); i < n; ++i)
{
trackers.emplace(tor, tr_torrentTracker(tor, i));
}
}
/* step 2: mark all the trackers in the list as not-updated */
@ -2116,61 +2107,49 @@ void DetailsDialog::Impl::refreshTracker(std::vector<tr_torrent*> const& torrent
row[tracker_cols.was_updated] = false;
}
/* step 3: add any new trackers */
for (size_t i = 0; i < statCount.size(); ++i)
/* step 3: add / update trackers */
for (auto const& [tor, tracker] : trackers)
{
int const jn = statCount.at(i);
auto const torrent_id = tr_torrentId(tor);
for (int j = 0; j < jn; ++j)
// build the key to find the row
gstr.str({});
gstr << torrent_id << '\t' << tracker.tier << '\t' << tracker.announce;
if (hash.find(gstr.str()) == hash.end())
{
tr_torrent const* tor = torrents.at(i);
tr_tracker_stat const* st = &stats.at(i)[j];
int const torrent_id = tr_torrentId(tor);
// if we didn't have that row, add it
auto const iter = store->append();
(*iter)[tracker_cols.torrent_id] = torrent_id;
(*iter)[tracker_cols.tracker_id] = tracker.id;
(*iter)[tracker_cols.key] = gstr.str();
/* build the key to find the row */
gstr.str({});
gstr << torrent_id << '\t' << st->tier << '\t' << st->announce;
if (hash.find(gstr.str()) == hash.end())
{
auto const iter = store->append();
(*iter)[tracker_cols.torrent_id] = torrent_id;
(*iter)[tracker_cols.tracker_id] = st->id;
(*iter)[tracker_cols.key] = gstr.str();
auto const p = store->get_path(iter);
hash.emplace(gstr.str(), Gtk::TreeRowReference(store, p));
gtr_get_favicon_from_url(
session,
st->announce,
[ref = Gtk::TreeRowReference(store, p)](auto const& pixbuf) mutable { favicon_ready_cb(pixbuf, ref); });
}
auto const p = store->get_path(iter);
hash.emplace(gstr.str(), Gtk::TreeRowReference(store, p));
gtr_get_favicon_from_url(
session,
tracker.announce,
[ref = Gtk::TreeRowReference(store, p)](auto const& pixbuf) mutable { favicon_ready_cb(pixbuf, ref); });
}
}
/* step 4: update the peers */
for (size_t i = 0; i < torrents.size(); ++i)
/* step 4: update the rows */
auto const summary_name = std::string(std::size(torrents) == 1 ? tr_torrentName(torrents.front()) : "");
for (auto const& [tor, tracker] : trackers)
{
tr_torrent const* tor = torrents.at(i);
auto const summary_name = std::string(torrents.size() > 1 ? tr_torrentName(tor) : "");
auto const torrent_id = tr_torrentId(tor);
for (int j = 0; j < statCount.at(i); ++j)
{
tr_tracker_stat const* st = &stats.at(i)[j];
// build the key to find the row
gstr.str({});
gstr << torrent_id << '\t' << tracker.tier << '\t' << tracker.announce;
auto const iter = store->get_iter(hash.at(gstr.str()).get_path());
/* build the key to find the row */
gstr.str({});
gstr << tr_torrentId(tor) << '\t' << st->tier << '\t' << st->announce;
auto const iter = store->get_iter(hash.at(gstr.str()).get_path());
/* update the row */
gstr.str({});
buildTrackerSummary(gstr, summary_name, st, showScrape, dialog_.get_direction());
(*iter)[tracker_cols.text] = gstr.str();
(*iter)[tracker_cols.is_backup] = st->isBackup;
(*iter)[tracker_cols.tracker_id] = st->id;
(*iter)[tracker_cols.was_updated] = true;
}
// update the row
gstr.str({});
buildTrackerSummary(gstr, summary_name, tracker, showScrape, dialog_.get_direction());
(*iter)[tracker_cols.text] = gstr.str();
(*iter)[tracker_cols.is_backup] = tracker.isBackup;
(*iter)[tracker_cols.tracker_id] = tracker.id;
(*iter)[tracker_cols.was_updated] = true;
}
/* step 5: remove trackers that have disappeared */
@ -2192,12 +2171,6 @@ void DetailsDialog::Impl::refreshTracker(std::vector<tr_torrent*> const& torrent
}
edit_trackers_button_->set_sensitive(tracker_list_get_current_torrent_id() >= 0);
/* cleanup */
for (size_t i = 0; i < stats.size(); ++i)
{
tr_torrentTrackersFree(stats[i], statCount[i]);
}
}
void DetailsDialog::Impl::onScrapeToggled()
@ -2280,19 +2253,18 @@ std::string get_editable_tracker_list(tr_torrent const* tor)
{
std::ostringstream gstr;
int tier = 0;
tr_info const* inf = tr_torrentInfo(tor);
for (unsigned int i = 0; i < inf->trackerCount; ++i)
for (size_t i = 0, n = tr_torrentTrackerCount(tor); i < n; ++i)
{
tr_tracker_info const* t = &inf->trackers[i];
auto const tracker = tr_torrentTracker(tor, i);
if (tier != t->tier)
if (tier != tracker.tier)
{
tier = t->tier;
tier = tracker.tier;
gstr << '\n';
}
gstr << t->announce << '\n';
gstr << tracker.announce << '\n';
}
auto str = gstr.str();
@ -2655,8 +2627,7 @@ void DetailsDialog::Impl::set_torrents(std::vector<int> const& ids)
{
int const id = ids.front();
auto const* tor = core_->find_torrent(id);
auto const* inf = tr_torrentInfo(tor);
title = gtr_sprintf(_("%s Properties"), inf->name);
title = gtr_sprintf(_("%s Properties"), tr_torrentName(tor));
file_list_->set_torrent(id);
file_list_->show();

View File

@ -425,7 +425,7 @@ void buildTree(FileRowNode& node, build_data& build)
auto const mime_type = isLeaf ? gtr_get_mime_type_from_filename(child_data.name) : DIRECTORY_MIME_TYPE;
auto const icon = gtr_get_mime_type_icon(mime_type, Gtk::ICON_SIZE_MENU, *build.w);
auto const file = tr_torrentFile(build.tor, child_data.index);
auto const file = isLeaf ? tr_torrentFile(build.tor, child_data.index) : tr_file_view{};
int const priority = isLeaf ? file.priority : 0;
bool const enabled = isLeaf ? file.wanted : true;
auto name_esc = Glib::Markup::escape_text(child_data.name);

View File

@ -174,13 +174,12 @@ bool tracker_filter_model_update(Glib::RefPtr<Gtk::TreeStore> const& tracker_mod
for (auto const& row : tmodel->children())
{
auto const* tor = static_cast<tr_torrent const*>(row.get_value(torrent_cols.torrent));
auto const* const inf = tr_torrentInfo(tor);
std::set<std::string const*> keys;
for (unsigned int i = 0; i < inf->trackerCount; ++i)
for (size_t i = 0, n = tr_torrentTrackerCount(tor); i < n; ++i)
{
auto const* const key = &*strings.insert(gtr_get_host_from_url(inf->trackers[i].announce)).first;
auto const* const key = &*strings.insert(gtr_get_host_from_url(tr_torrentTracker(tor, i).announce)).first;
if (auto const count = hosts_hash.find(key); count == hosts_hash.end())
{
@ -384,21 +383,20 @@ namespace
bool test_tracker(tr_torrent const* tor, int active_tracker_type, Glib::ustring const& host)
{
bool matches = true;
if (active_tracker_type == TRACKER_FILTER_TYPE_HOST)
if (active_tracker_type != TRACKER_FILTER_TYPE_HOST)
{
auto const* const inf = tr_torrentInfo(tor);
return true;
}
matches = false;
for (unsigned int i = 0; !matches && i < inf->trackerCount; ++i)
for (size_t i = 0, n = tr_torrentTrackerCount(tor); i < n; ++i)
{
if (gtr_get_host_from_url(tr_torrentTracker(tor, i).announce) == host)
{
matches = gtr_get_host_from_url(inf->trackers[i].announce) == host;
return true;
}
}
return matches;
return false;
}
/***

View File

@ -456,15 +456,9 @@ int compare_by_activity(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::itera
int compare_by_age(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator const& b)
{
int ret = 0;
auto* const ta = static_cast<tr_torrent*>(a->get_value(torrent_cols.torrent));
auto* const tb = static_cast<tr_torrent*>(b->get_value(torrent_cols.torrent));
if (ret == 0)
{
ret = compare_time(tr_torrentStatCached(ta)->addedDate, tr_torrentStatCached(tb)->addedDate);
}
int ret = compare_time(tr_torrentStatCached(ta)->addedDate, tr_torrentStatCached(tb)->addedDate);
if (ret == 0)
{
@ -476,15 +470,9 @@ int compare_by_age(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator c
int compare_by_size(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator const& b)
{
int ret = 0;
auto const* const ia = tr_torrentInfo(static_cast<tr_torrent*>(a->get_value(torrent_cols.torrent)));
auto const* const ib = tr_torrentInfo(static_cast<tr_torrent*>(b->get_value(torrent_cols.torrent)));
if (ret == 0)
{
ret = compare_uint64(ia->totalSize, ib->totalSize);
}
auto const size_a = tr_torrentInfo(static_cast<tr_torrent*>(a->get_value(torrent_cols.torrent)))->totalSize;
auto const size_b = tr_torrentInfo(static_cast<tr_torrent*>(b->get_value(torrent_cols.torrent)))->totalSize;
int ret = compare_uint64(size_a, size_b);
if (ret == 0)
{
@ -496,15 +484,9 @@ int compare_by_size(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator
int compare_by_progress(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator const& b)
{
int ret = 0;
auto const* const sa = tr_torrentStatCached(static_cast<tr_torrent*>(a->get_value(torrent_cols.torrent)));
auto const* const sb = tr_torrentStatCached(static_cast<tr_torrent*>(b->get_value(torrent_cols.torrent)));
if (ret == 0)
{
ret = compare_double(sa->percentComplete, sb->percentComplete);
}
int ret = compare_double(sa->percentComplete, sb->percentComplete);
if (ret == 0)
{
@ -521,15 +503,9 @@ int compare_by_progress(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::itera
int compare_by_eta(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator const& b)
{
int ret = 0;
auto const* const sa = tr_torrentStatCached(static_cast<tr_torrent*>(a->get_value(torrent_cols.torrent)));
auto const* const sb = tr_torrentStatCached(static_cast<tr_torrent*>(b->get_value(torrent_cols.torrent)));
if (ret == 0)
{
ret = compare_eta(sa->eta, sb->eta);
}
int ret = compare_eta(sa->eta, sb->eta);
if (ret == 0)
{
@ -541,15 +517,9 @@ int compare_by_eta(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator c
int compare_by_state(Gtk::TreeModel::iterator const& a, Gtk::TreeModel::iterator const& b)
{
int ret = 0;
auto const sa = a->get_value(torrent_cols.activity);
auto const sb = b->get_value(torrent_cols.activity);
if (ret == 0)
{
ret = compare_int(sa, sb);
}
int ret = compare_int(sa, sb);
if (ret == 0)
{
@ -893,8 +863,7 @@ namespace
Glib::ustring get_collated_name(tr_torrent const* tor)
{
auto const* const inf = tr_torrentInfo(tor);
return gtr_sprintf("%s\t%s", Glib::ustring(tr_torrentName(tor)).lowercase(), inf->hashString);
return gtr_sprintf("%s\t%s", Glib::ustring(tr_torrentName(tor)).lowercase(), tr_torrentInfo(tor)->hashString);
}
struct metadata_callback_data
@ -951,14 +920,13 @@ namespace
unsigned int build_torrent_trackers_hash(tr_torrent* tor)
{
uint64_t hash = 0;
tr_info const* const inf = tr_torrentInfo(tor);
auto hash = uint64_t{};
for (unsigned int i = 0; i < inf->trackerCount; ++i)
for (size_t i = 0, n = tr_torrentTrackerCount(tor); i < n; ++i)
{
for (char const* pch = inf->trackers[i].announce; *pch != '\0'; ++pch)
for (auto const ch : std::string_view{ tr_torrentTracker(tor, i).announce })
{
hash = (hash << 4) ^ (hash >> 28) ^ *pch;
hash = (hash << 4) ^ (hash >> 28) ^ ch;
}
}

View File

@ -33,13 +33,13 @@
namespace
{
Glib::ustring getProgressString(tr_torrent const* tor, tr_info const* info, tr_stat const* st)
Glib::ustring getProgressString(tr_torrent const* tor, uint64_t total_size, tr_stat const* st)
{
Glib::ustring gstr;
bool const isDone = st->leftUntilDone == 0;
uint64_t const haveTotal = st->haveUnchecked + st->haveValid;
bool const isSeed = st->haveValid >= info->totalSize;
bool const isSeed = st->haveValid >= total_size;
double seedRatio;
bool const hasSeedRatio = tr_torrentGetSeedRatio(tor, &seedRatio);
@ -67,7 +67,7 @@ Glib::ustring getProgressString(tr_torrent const* tor, tr_info const* info, tr_s
%6$s is the ratio we want to reach before we stop uploading */
_("%1$s of %2$s (%3$s%%), uploaded %4$s (Ratio: %5$s Goal: %6$s)"),
tr_strlsize(haveTotal),
tr_strlsize(info->totalSize),
tr_strlsize(total_size),
tr_strlpercent(st->percentComplete * 100.0),
tr_strlsize(st->uploadedEver),
tr_strlratio(st->ratio),
@ -83,7 +83,7 @@ Glib::ustring getProgressString(tr_torrent const* tor, tr_info const* info, tr_s
%5$s is our upload-to-download ratio */
_("%1$s of %2$s (%3$s%%), uploaded %4$s (Ratio: %5$s)"),
tr_strlsize(haveTotal),
tr_strlsize(info->totalSize),
tr_strlsize(total_size),
tr_strlpercent(st->percentComplete * 100.0),
tr_strlsize(st->uploadedEver),
tr_strlratio(st->ratio));
@ -99,7 +99,7 @@ Glib::ustring getProgressString(tr_torrent const* tor, tr_info const* info, tr_s
%3$s is our upload-to-download ratio,
%4$s is the ratio we want to reach before we stop uploading */
_("%1$s, uploaded %2$s (Ratio: %3$s Goal: %4$s)"),
tr_strlsize(info->totalSize),
tr_strlsize(total_size),
tr_strlsize(st->uploadedEver),
tr_strlratio(st->ratio),
tr_strlratio(seedRatio));
@ -111,7 +111,7 @@ Glib::ustring getProgressString(tr_torrent const* tor, tr_info const* info, tr_s
%2$s is how much we've uploaded,
%3$s is our upload-to-download ratio */
_("%1$s, uploaded %2$s (Ratio: %3$s)"),
tr_strlsize(info->totalSize),
tr_strlsize(total_size),
tr_strlsize(st->uploadedEver),
tr_strlratio(st->ratio));
}
@ -458,12 +458,12 @@ void TorrentCellRenderer::Impl::get_size_full(Gtk::Widget& widget, int& width, i
auto* const tor = static_cast<tr_torrent*>(torrent.get_value());
auto const* const st = tr_torrentStatCached(tor);
auto const* const inf = tr_torrentInfo(tor);
auto const total_size = tr_torrentInfo(tor)->totalSize;
auto const icon = get_icon(tor, FULL_ICON_SIZE, widget);
auto const name = Glib::ustring(tr_torrentName(tor));
auto const gstr_stat = getStatusString(tor, st, upload_speed_KBps.get_value(), download_speed_KBps.get_value());
auto const gstr_prog = getProgressString(tor, inf, st);
auto const gstr_prog = getProgressString(tor, total_size, st);
renderer_.get_padding(xpad, ypad);
/* get the idealized cell dimensions */
@ -677,7 +677,7 @@ void TorrentCellRenderer::Impl::render_full(
auto* const tor = static_cast<tr_torrent*>(torrent.get_value());
auto const* const st = tr_torrentStatCached(tor);
auto const* const inf = tr_torrentInfo(tor);
auto const total_size = tr_torrentInfo(tor)->totalSize;
bool const active = st->activity != TR_STATUS_STOPPED && st->activity != TR_STATUS_DOWNLOAD_WAIT &&
st->activity != TR_STATUS_SEED_WAIT;
auto const percentDone = get_percent_done(tor, st, &seed);
@ -685,7 +685,7 @@ void TorrentCellRenderer::Impl::render_full(
auto const icon = get_icon(tor, FULL_ICON_SIZE, widget);
auto const name = Glib::ustring(tr_torrentName(tor));
auto const gstr_prog = getProgressString(tor, inf, st);
auto const gstr_prog = getProgressString(tor, total_size, st);
auto const gstr_stat = getStatusString(tor, st, upload_speed_KBps.get_value(), download_speed_KBps.get_value());
renderer_.get_padding(xpad, ypad);
auto const text_color = get_text_color(widget, st);

View File

@ -1713,115 +1713,120 @@ static void onUpkeepTimer(evutil_socket_t /*fd*/, short /*what*/, void* vannounc
****
***/
tr_tracker_stat* tr_announcerStats(tr_torrent const* torrent, int* setmeTrackerCount)
static tr_tracker_view trackerView(tr_torrent const& tor, int tier_index, tr_tier const& tier, tr_tracker const& tracker)
{
TR_ASSERT(tr_isTorrent(torrent));
auto const now = tr_time();
auto view = tr_tracker_view{};
time_t const now = tr_time();
view.host = tr_quark_get_string(tracker.key);
view.announce = tr_quark_get_string(tracker.announce_url);
view.scrape = tracker.scrape_info == nullptr ? "" : tr_quark_get_string(tracker.scrape_info->scrape_url);
int out = 0;
struct tr_torrent_tiers const* const tt = torrent->tiers;
view.id = tracker.id;
view.tier = tier_index;
view.isBackup = &tracker != tier.currentTracker;
view.lastScrapeStartTime = tier.lastScrapeStartTime;
view.seederCount = tracker.seederCount;
view.leecherCount = tracker.leecherCount;
view.downloadCount = tracker.downloadCount;
/* alloc the stats */
*setmeTrackerCount = tt->tracker_count;
tr_tracker_stat* const ret = tr_new0(tr_tracker_stat, tt->tracker_count);
/* populate the stats */
for (int i = 0; i < tt->tier_count; ++i)
if (view.isBackup)
{
tr_tier const* const tier = &tt->tiers[i];
for (int j = 0; j < tier->tracker_count; ++j)
view.scrapeState = TR_TRACKER_INACTIVE;
view.announceState = TR_TRACKER_INACTIVE;
view.nextScrapeTime = 0;
view.nextAnnounceTime = 0;
}
else
{
view.hasScraped = tier.lastScrapeTime;
if (view.hasScraped != 0)
{
tr_tracker const* const tracker = &tier->trackers[j];
tr_tracker_stat* st = &ret[out++];
view.lastScrapeTime = tier.lastScrapeTime;
view.lastScrapeSucceeded = tier.lastScrapeSucceeded;
view.lastScrapeTimedOut = tier.lastScrapeTimedOut;
tr_strlcpy(view.lastScrapeResult, tier.lastScrapeStr, sizeof(view.lastScrapeResult));
}
st->id = tracker->id;
st->host = tr_quark_get_string(tracker->key);
st->announce = tr_quark_get_string(tracker->announce_url);
st->tier = i;
st->isBackup = tracker != tier->currentTracker;
st->lastScrapeStartTime = tier->lastScrapeStartTime;
st->scrape = tracker->scrape_info == nullptr ? "" : tr_quark_get_string(tracker->scrape_info->scrape_url);
st->seederCount = tracker->seederCount;
st->leecherCount = tracker->leecherCount;
st->downloadCount = tracker->downloadCount;
if (tier.isScraping)
{
view.scrapeState = TR_TRACKER_ACTIVE;
}
else if (tier.scrapeAt == 0)
{
view.scrapeState = TR_TRACKER_INACTIVE;
}
else if (tier.scrapeAt > now)
{
view.scrapeState = TR_TRACKER_WAITING;
view.nextScrapeTime = tier.scrapeAt;
}
else
{
view.scrapeState = TR_TRACKER_QUEUED;
}
if (st->isBackup)
{
st->scrapeState = TR_TRACKER_INACTIVE;
st->announceState = TR_TRACKER_INACTIVE;
st->nextScrapeTime = 0;
st->nextAnnounceTime = 0;
}
else
{
st->hasScraped = tier->lastScrapeTime;
if (st->hasScraped != 0)
{
st->lastScrapeTime = tier->lastScrapeTime;
st->lastScrapeSucceeded = tier->lastScrapeSucceeded;
st->lastScrapeTimedOut = tier->lastScrapeTimedOut;
tr_strlcpy(st->lastScrapeResult, tier->lastScrapeStr, sizeof(st->lastScrapeResult));
}
view.lastAnnounceStartTime = tier.lastAnnounceStartTime;
if (tier->isScraping)
{
st->scrapeState = TR_TRACKER_ACTIVE;
}
else if (tier->scrapeAt == 0)
{
st->scrapeState = TR_TRACKER_INACTIVE;
}
else if (tier->scrapeAt > now)
{
st->scrapeState = TR_TRACKER_WAITING;
st->nextScrapeTime = tier->scrapeAt;
}
else
{
st->scrapeState = TR_TRACKER_QUEUED;
}
view.hasAnnounced = tier.lastAnnounceTime;
if (view.hasAnnounced != 0)
{
view.lastAnnounceTime = tier.lastAnnounceTime;
view.lastAnnounceSucceeded = tier.lastAnnounceSucceeded;
view.lastAnnounceTimedOut = tier.lastAnnounceTimedOut;
view.lastAnnouncePeerCount = tier.lastAnnouncePeerCount;
tr_strlcpy(view.lastAnnounceResult, tier.lastAnnounceStr, sizeof(view.lastAnnounceResult));
}
st->lastAnnounceStartTime = tier->lastAnnounceStartTime;
st->hasAnnounced = tier->lastAnnounceTime;
if (st->hasAnnounced != 0)
{
st->lastAnnounceTime = tier->lastAnnounceTime;
tr_strlcpy(st->lastAnnounceResult, tier->lastAnnounceStr, sizeof(st->lastAnnounceResult));
st->lastAnnounceSucceeded = tier->lastAnnounceSucceeded;
st->lastAnnounceTimedOut = tier->lastAnnounceTimedOut;
st->lastAnnouncePeerCount = tier->lastAnnouncePeerCount;
}
if (tier->isAnnouncing)
{
st->announceState = TR_TRACKER_ACTIVE;
}
else if (!torrent->isRunning || tier->announceAt == 0)
{
st->announceState = TR_TRACKER_INACTIVE;
}
else if (tier->announceAt > now)
{
st->announceState = TR_TRACKER_WAITING;
st->nextAnnounceTime = tier->announceAt;
}
else
{
st->announceState = TR_TRACKER_QUEUED;
}
}
if (tier.isAnnouncing)
{
view.announceState = TR_TRACKER_ACTIVE;
}
else if (!tor.isRunning || tier.announceAt == 0)
{
view.announceState = TR_TRACKER_INACTIVE;
}
else if (tier.announceAt > now)
{
view.announceState = TR_TRACKER_WAITING;
view.nextAnnounceTime = tier.announceAt;
}
else
{
view.announceState = TR_TRACKER_QUEUED;
}
}
return ret;
TR_ASSERT(0 <= view.tier);
TR_ASSERT(view.tier < tor.tiers->tier_count);
return view;
}
void tr_announcerStatsFree(tr_tracker_stat* trackers, int /*trackerCount*/)
tr_tracker_view tr_announcerTracker(tr_torrent const* tor, size_t nth)
{
tr_free(trackers);
TR_ASSERT(tr_isTorrent(tor));
TR_ASSERT(tor->tiers != nullptr);
// find the nth tracker
struct tr_torrent_tiers const* const tt = tor->tiers;
if (nth >= size_t(tt->tracker_count))
{
return {};
}
auto const& tracker = tt->trackers[nth];
for (int i = 0; i < tt->tier_count; ++i)
{
tr_tier const& tier = tt->tiers[i];
for (int j = 0; j < tier.tracker_count; ++j)
{
if (&tier.trackers[j] == &tracker)
{
return trackerView(*tor, i, tier, tracker);
}
}
}
return {};
}
/***

View File

@ -93,9 +93,7 @@ void tr_announcerAddBytes(tr_torrent*, int up_down_or_corrupt, uint32_t byteCoun
time_t tr_announcerNextManualAnnounce(tr_torrent const*);
tr_tracker_stat* tr_announcerStats(tr_torrent const* torrent, int* setmeTrackerCount);
void tr_announcerStatsFree(tr_tracker_stat* trackers, int trackerCount);
tr_tracker_view tr_announcerTracker(tr_torrent const* torrent, size_t i);
/***
****

View File

@ -128,6 +128,8 @@ void tr_file_priorities::set(tr_file_index_t const* files, size_t n, tr_priority
tr_priority_t tr_file_priorities::filePriority(tr_file_index_t file) const
{
TR_ASSERT(file < std::size(priorities_));
return priorities_[file];
}

View File

@ -1704,36 +1704,14 @@ uint64_t tr_peerMgrGetDesiredAvailable(tr_torrent const* tor)
return desired_available;
}
double* tr_peerMgrWebSpeeds_KBps(tr_torrent const* tor)
tr_webseed_view tr_peerMgrWebseed(tr_torrent const* tor, size_t i)
{
TR_ASSERT(tr_isTorrent(tor));
TR_ASSERT(tor->swarm != nullptr);
size_t const n = tr_ptrArraySize(&tor->swarm->webseeds);
TR_ASSERT(i < n);
auto const now = tr_time_msec();
tr_swarm* const s = tor->swarm;
TR_ASSERT(s->manager != nullptr);
unsigned int n = tr_ptrArraySize(&s->webseeds);
TR_ASSERT(n == tor->info.webseedCount);
double* ret = tr_new0(double, n);
for (unsigned int i = 0; i < n; ++i)
{
unsigned int Bps = 0;
auto const* const peer = static_cast<tr_peer*>(tr_ptrArrayNth(&s->webseeds, i));
if (peer->is_transferring_pieces(now, TR_DOWN, &Bps))
{
ret[i] = Bps / (double)tr_speed_K;
}
else
{
ret[i] = -1.0;
}
}
return ret;
return i >= n ? tr_webseed_view{} : tr_webseedView(static_cast<tr_peer const*>(tr_ptrArrayNth(&tor->swarm->webseeds, i)));
}
static auto getPeerStats(tr_peerMsgs const* peer, time_t now, uint64_t now_msec)

View File

@ -138,7 +138,7 @@ void tr_peerMgrOnBlocklistChanged(tr_peerMgr* manager);
struct tr_peer_stat* tr_peerMgrPeerStats(tr_torrent const* tor, int* setmeCount);
double* tr_peerMgrWebSpeeds_KBps(tr_torrent const* tor);
tr_webseed_view tr_peerMgrWebseed(tr_torrent const* tor, size_t i);
unsigned int tr_peerGetPieceSpeed_Bps(tr_peer const* peer, uint64_t now, tr_direction direction);

View File

@ -1750,6 +1750,11 @@ static ReadState readBtMessage(tr_peerMsgsImpl* msgs, struct evbuffer* inbuf, si
{
tr_removeElementFromArray(msgs->peerAskedFor, i, sizeof(struct peer_request), msgs->pendingReqsToClient);
--msgs->pendingReqsToClient;
if (fext)
{
protocolSendReject(msgs, &r);
}
break;
}
}

View File

@ -375,6 +375,12 @@ static uint64_t loadName(tr_variant* dict, tr_torrent* tor)
return 0;
}
name = tr_strvDup(name);
if (std::empty(name))
{
return 0;
}
if (name != tr_torrentName(tor))
{
tr_free(tor->info.name);

View File

@ -426,39 +426,35 @@ static void addTrackers(tr_info const* info, tr_variant* trackers)
}
}
static void addTrackerStats(tr_tracker_stat const* st, int n, tr_variant* list)
static void addTrackerStats(tr_tracker_view const& tracker, tr_variant* list)
{
for (int i = 0; i < n; ++i)
{
tr_tracker_stat const* s = &st[i];
tr_variant* d = tr_variantListAddDict(list, 26);
tr_variantDictAddStr(d, TR_KEY_announce, s->announce);
tr_variantDictAddInt(d, TR_KEY_announceState, s->announceState);
tr_variantDictAddInt(d, TR_KEY_downloadCount, s->downloadCount);
tr_variantDictAddBool(d, TR_KEY_hasAnnounced, s->hasAnnounced);
tr_variantDictAddBool(d, TR_KEY_hasScraped, s->hasScraped);
tr_variantDictAddStr(d, TR_KEY_host, s->host);
tr_variantDictAddInt(d, TR_KEY_id, s->id);
tr_variantDictAddBool(d, TR_KEY_isBackup, s->isBackup);
tr_variantDictAddInt(d, TR_KEY_lastAnnouncePeerCount, s->lastAnnouncePeerCount);
tr_variantDictAddStr(d, TR_KEY_lastAnnounceResult, s->lastAnnounceResult);
tr_variantDictAddInt(d, TR_KEY_lastAnnounceStartTime, s->lastAnnounceStartTime);
tr_variantDictAddBool(d, TR_KEY_lastAnnounceSucceeded, s->lastAnnounceSucceeded);
tr_variantDictAddInt(d, TR_KEY_lastAnnounceTime, s->lastAnnounceTime);
tr_variantDictAddBool(d, TR_KEY_lastAnnounceTimedOut, s->lastAnnounceTimedOut);
tr_variantDictAddStr(d, TR_KEY_lastScrapeResult, s->lastScrapeResult);
tr_variantDictAddInt(d, TR_KEY_lastScrapeStartTime, s->lastScrapeStartTime);
tr_variantDictAddBool(d, TR_KEY_lastScrapeSucceeded, s->lastScrapeSucceeded);
tr_variantDictAddInt(d, TR_KEY_lastScrapeTime, s->lastScrapeTime);
tr_variantDictAddBool(d, TR_KEY_lastScrapeTimedOut, s->lastScrapeTimedOut);
tr_variantDictAddInt(d, TR_KEY_leecherCount, s->leecherCount);
tr_variantDictAddInt(d, TR_KEY_nextAnnounceTime, s->nextAnnounceTime);
tr_variantDictAddInt(d, TR_KEY_nextScrapeTime, s->nextScrapeTime);
tr_variantDictAddStr(d, TR_KEY_scrape, s->scrape);
tr_variantDictAddInt(d, TR_KEY_scrapeState, s->scrapeState);
tr_variantDictAddInt(d, TR_KEY_seederCount, s->seederCount);
tr_variantDictAddInt(d, TR_KEY_tier, s->tier);
}
auto* const d = tr_variantListAddDict(list, 26);
tr_variantDictAddStr(d, TR_KEY_announce, tracker.announce);
tr_variantDictAddInt(d, TR_KEY_announceState, tracker.announceState);
tr_variantDictAddInt(d, TR_KEY_downloadCount, tracker.downloadCount);
tr_variantDictAddBool(d, TR_KEY_hasAnnounced, tracker.hasAnnounced);
tr_variantDictAddBool(d, TR_KEY_hasScraped, tracker.hasScraped);
tr_variantDictAddStr(d, TR_KEY_host, tracker.host);
tr_variantDictAddInt(d, TR_KEY_id, tracker.id);
tr_variantDictAddBool(d, TR_KEY_isBackup, tracker.isBackup);
tr_variantDictAddInt(d, TR_KEY_lastAnnouncePeerCount, tracker.lastAnnouncePeerCount);
tr_variantDictAddStr(d, TR_KEY_lastAnnounceResult, tracker.lastAnnounceResult);
tr_variantDictAddInt(d, TR_KEY_lastAnnounceStartTime, tracker.lastAnnounceStartTime);
tr_variantDictAddBool(d, TR_KEY_lastAnnounceSucceeded, tracker.lastAnnounceSucceeded);
tr_variantDictAddInt(d, TR_KEY_lastAnnounceTime, tracker.lastAnnounceTime);
tr_variantDictAddBool(d, TR_KEY_lastAnnounceTimedOut, tracker.lastAnnounceTimedOut);
tr_variantDictAddStr(d, TR_KEY_lastScrapeResult, tracker.lastScrapeResult);
tr_variantDictAddInt(d, TR_KEY_lastScrapeStartTime, tracker.lastScrapeStartTime);
tr_variantDictAddBool(d, TR_KEY_lastScrapeSucceeded, tracker.lastScrapeSucceeded);
tr_variantDictAddInt(d, TR_KEY_lastScrapeTime, tracker.lastScrapeTime);
tr_variantDictAddBool(d, TR_KEY_lastScrapeTimedOut, tracker.lastScrapeTimedOut);
tr_variantDictAddInt(d, TR_KEY_leecherCount, tracker.leecherCount);
tr_variantDictAddInt(d, TR_KEY_nextAnnounceTime, tracker.nextAnnounceTime);
tr_variantDictAddInt(d, TR_KEY_nextScrapeTime, tracker.nextScrapeTime);
tr_variantDictAddStr(d, TR_KEY_scrape, tracker.scrape);
tr_variantDictAddInt(d, TR_KEY_scrapeState, tracker.scrapeState);
tr_variantDictAddInt(d, TR_KEY_seederCount, tracker.seederCount);
tr_variantDictAddInt(d, TR_KEY_tier, tracker.tier);
}
static void addPeers(tr_torrent* tor, tr_variant* list)
@ -791,11 +787,13 @@ static void initField(
case TR_KEY_trackerStats:
{
auto n = int{};
tr_tracker_stat* s = tr_torrentTrackers(tor, &n);
auto const n = tr_torrentTrackerCount(tor);
tr_variantInitList(initme, n);
addTrackerStats(s, n, initme);
tr_torrentTrackersFree(s, n);
for (size_t i = 0; i < n; ++i)
{
auto const& tracker = tr_torrentTracker(tor, i);
addTrackerStats(tracker, initme);
}
break;
}

View File

@ -1241,17 +1241,32 @@ size_t tr_torrentFileCount(tr_torrent const* torrent)
return torrent->fileCount();
}
/***
****
***/
tr_webseed_view tr_torrentWebseed(tr_torrent const* tor, size_t i)
{
return tr_peerMgrWebseed(tor, i);
}
double* tr_torrentWebSpeeds_KBps(tr_torrent const* tor)
size_t tr_torrentWebseedCount(tr_torrent const* tor)
{
TR_ASSERT(tr_isTorrent(tor));
return tr_peerMgrWebSpeeds_KBps(tor);
return tor->webseedCount();
}
tr_tracker_view tr_torrentTracker(tr_torrent const* tor, size_t i)
{
return tr_announcerTracker(tor, i);
}
size_t tr_torrentTrackerCount(tr_torrent const* tor)
{
return tor->trackerCount();
}
/***
****
***/
tr_peer_stat* tr_torrentPeers(tr_torrent const* tor, int* peerCount)
{
TR_ASSERT(tr_isTorrent(tor));
@ -1264,18 +1279,6 @@ void tr_torrentPeersFree(tr_peer_stat* peers, int /*peerCount*/)
tr_free(peers);
}
tr_tracker_stat* tr_torrentTrackers(tr_torrent const* tor, int* setmeTrackerCount)
{
TR_ASSERT(tr_isTorrent(tor));
return tr_announcerStats(tor, setmeTrackerCount);
}
void tr_torrentTrackersFree(tr_tracker_stat* trackers, int trackerCount)
{
tr_announcerStatsFree(trackers, trackerCount);
}
void tr_torrentAvailability(tr_torrent const* tor, int8_t* tab, int size)
{
TR_ASSERT(tr_isTorrent(tor));
@ -1817,20 +1820,15 @@ static std::string buildTrackersString(tr_torrent const* tor)
{
auto buf = std::stringstream{};
int n = 0;
tr_tracker_stat* stats = tr_torrentTrackers(tor, &n);
for (int i = 0; i < n;)
for (size_t i = 0, n = tr_torrentTrackerCount(tor); i < n; ++i)
{
tr_tracker_stat const* s = &stats[i];
buf << s->host;
buf << tr_torrentTracker(tor, i).host;
if (++i < n)
{
buf << ',';
}
}
tr_torrentTrackersFree(stats, n);
return buf.str();
}

View File

@ -327,6 +327,34 @@ public:
std::optional<tr_found_file_t> findFile(std::string& filename, tr_file_index_t i) const;
/// WEBSEEDS
auto webseedCount() const
{
return info.webseedCount;
}
auto const& webseed(size_t i) const
{
TR_ASSERT(i < webseedCount());
return info.webseeds[i];
}
auto& webseed(size_t i)
{
TR_ASSERT(i < webseedCount());
return info.webseeds[i];
}
/// TRACKERS
auto trackerCount() const
{
return info.trackerCount;
}
/// CHECKSUMS
bool ensurePieceIsChecked(tr_piece_index_t piece)

View File

@ -1403,115 +1403,50 @@ enum tr_tracker_state
TR_TRACKER_ACTIVE = 3
};
struct tr_tracker_stat
/*
* Unlike other _view structs, it is safe to keep a tr_tracker_view copy.
* The announce, scrape, and host strings are interned & never go out-of-scope.
*/
struct tr_tracker_view
{
/* how many downloads this tracker knows of (-1 means it does not know) */
int downloadCount;
char const* announce; // full announce URL
char const* scrape; // full scrape URL
char const* host; // human-readable tracker name. (`${host}:${port}`)
/* whether or not we've ever sent this tracker an announcement */
bool hasAnnounced;
char lastAnnounceResult[128]; // if hasAnnounced, the human-readable result of latest announce
char lastScrapeResult[128]; // if hasScraped, the human-readable result of the latest scrape
/* whether or not we've ever scraped to this tracker */
bool hasScraped;
time_t lastAnnounceStartTime; // if hasAnnounced, when the latest announce request was sent
time_t lastAnnounceTime; // if hasAnnounced, when the latest announce reply was received
time_t nextAnnounceTime; // if announceState == TR_TRACKER_WAITING, time of next announce
/* human-readable string identifying the tracker.
* 'host' is a slight misnomer; the current format ist `$host:$port` */
char const* host;
time_t lastScrapeStartTime; // if hasScraped, when the latest scrape request was sent
time_t lastScrapeTime; // if hasScraped, when the latest scrape reply was received
time_t nextScrapeTime; // if scrapeState == TR_TRACKER_WAITING, time of next scrape
/* the full announce URL */
char const* announce;
int downloadCount; // number of times this torrent's been downloaded, or -1 if unknown
int lastAnnouncePeerCount; // if hasAnnounced, the number of peers the tracker gave us
int leecherCount; // number of leechers the tracker knows of, or -1 if unknown
int seederCount; // number of seeders the tracker knows of, or -1 if unknown
/* the full scrape URL */
char const* scrape;
int tier; // which tier this tracker is in
int id; // unique transmission-generated ID for use in libtransmission API
/* Transmission uses one tracker per tier,
* and the others are kept as backups */
bool isBackup;
tr_tracker_state announceState; // whether we're announcing, waiting to announce, etc.
tr_tracker_state scrapeState; // whether we're scraping, waiting to scrape, etc.
/* is the tracker announcing, waiting, queued, etc */
tr_tracker_state announceState;
/* is the tracker scraping, waiting, queued, etc */
tr_tracker_state scrapeState;
/* number of peers the tracker told us about last time.
* if "lastAnnounceSucceeded" is false, this field is undefined */
int lastAnnouncePeerCount;
/* human-readable string with the result of the last announce.
if "hasAnnounced" is false, this field is undefined */
char lastAnnounceResult[128];
/* when the last announce was sent to the tracker.
* if "hasAnnounced" is false, this field is undefined */
time_t lastAnnounceStartTime;
/* whether or not the last announce was a success.
if "hasAnnounced" is false, this field is undefined */
bool lastAnnounceSucceeded;
/* whether or not the last announce timed out. */
bool lastAnnounceTimedOut;
/* when the last announce was completed.
if "hasAnnounced" is false, this field is undefined */
time_t lastAnnounceTime;
/* human-readable string with the result of the last scrape.
* if "hasScraped" is false, this field is undefined */
char lastScrapeResult[128];
/* when the last scrape was sent to the tracker.
* if "hasScraped" is false, this field is undefined */
time_t lastScrapeStartTime;
/* whether or not the last scrape was a success.
if "hasAnnounced" is false, this field is undefined */
bool lastScrapeSucceeded;
/* whether or not the last scrape timed out. */
bool lastScrapeTimedOut;
/* when the last scrape was completed.
if "hasScraped" is false, this field is undefined */
time_t lastScrapeTime;
/* number of leechers this tracker knows of (-1 means it does not know) */
int leecherCount;
/* when the next periodic announce message will be sent out.
if announceState isn't TR_TRACKER_WAITING, this field is undefined */
time_t nextAnnounceTime;
/* when the next periodic scrape message will be sent out.
if scrapeState isn't TR_TRACKER_WAITING, this field is undefined */
time_t nextScrapeTime;
/* number of seeders this tracker knows of (-1 means it does not know) */
int seederCount;
/* which tier this tracker is in */
int tier;
/* used to match to a tr_tracker_info */
uint32_t id;
bool hasAnnounced; // true iff we've announced to this tracker during this session
bool hasScraped; // true iff we've scraped this tracker during this session
bool isBackup; // only one tracker per tier is used; the others are kept as backups
bool lastAnnounceSucceeded; // if hasAnnounced, whether or not the latest announce succeeded
bool lastAnnounceTimedOut; // true iff the latest announce request timed out
bool lastScrapeSucceeded; // if hasScraped, whether or not the latest scrape succeeded
bool lastScrapeTimedOut; // true iff the latest scrape request timed out
};
tr_tracker_stat* tr_torrentTrackers(tr_torrent const* torrent, int* setmeTrackerCount);
struct tr_tracker_view tr_torrentTracker(tr_torrent const* torrent, size_t i);
void tr_torrentTrackersFree(tr_tracker_stat* trackerStats, int trackerCount);
/**
* @brief get the download speeds for each of this torrent's webseed sources.
*
* @return an array of tor->info.webseedCount floats giving download speeds.
* Each speed in the array corresponds to the webseed at the same
* array index in tor->info.webseeds.
* To differentiate "idle" and "stalled" status, idle webseeds will
* return -1 instead of 0 KiB/s.
* NOTE: always free this array with tr_free() when you're done with it.
*/
double* tr_torrentWebSpeeds_KBps(tr_torrent const* torrent);
size_t tr_torrentTrackerCount(tr_torrent const* torrent);
/*
* This view structure is intended for short-term use. Its pointers are owned
@ -1530,6 +1465,21 @@ tr_file_view tr_torrentFile(tr_torrent const* torrent, tr_file_index_t file);
size_t tr_torrentFileCount(tr_torrent const* torrent);
/*
* This view structure is intended for short-term use. Its pointers are owned
* by the torrent and may be invalidated if the torrent is edited or removed.
*/
struct tr_webseed_view
{
char const* url; // the url to download from
bool is_downloading; // can be true even if speed is 0, e.g. slow download
unsigned download_bytes_per_second; // current download speed
};
struct tr_webseed_view tr_torrentWebseed(tr_torrent const* torrent, size_t nth);
size_t tr_torrentWebseedCount(tr_torrent const* torrent);
/***********************************************************************
* tr_torrentAvailability
***********************************************************************

View File

@ -125,6 +125,19 @@ public:
} // namespace
tr_webseed_view tr_webseedView(tr_peer const* peer)
{
auto const* w = dynamic_cast<tr_webseed const*>(peer);
if (w == nullptr)
{
return {};
}
auto bytes_per_second = unsigned{ 0 };
auto const is_downloading = peer->is_transferring_pieces(tr_time_msec(), TR_DOWN, &bytes_per_second);
return { w->base_url.c_str(), is_downloading, bytes_per_second };
}
/***
****
***/

View File

@ -17,3 +17,5 @@
#include "peer-common.h"
tr_peer* tr_webseedNew(struct tr_torrent* torrent, std::string_view, tr_peer_callback callback, void* callback_data);
tr_webseed_view tr_webseedView(tr_peer const* peer);

View File

@ -20,7 +20,10 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
#include <optional>
#include <libtransmission/transmission.h>
#include <libtransmission/error.h>
#include <libtransmission/log.h>
#include <libtransmission/utils.h> // tr_new()
@ -736,25 +739,25 @@ bool trashDataFile(char const* filename, tr_error** error)
- (NSMutableArray*)allTrackerStats
{
int count;
tr_tracker_stat* stats = tr_torrentTrackers(fHandle, &count);
auto const count = tr_torrentTrackerCount(fHandle);
auto tier = std::optional<int>{};
NSMutableArray* trackers = [NSMutableArray arrayWithCapacity:(count > 0 ? count + (stats[count - 1].tier + 1) : 0)];
NSMutableArray* trackers = [NSMutableArray arrayWithCapacity:count * 2];
int prevTier = -1;
for (int i = 0; i < count; ++i)
for (size_t i = 0; i < count; ++i)
{
if (stats[i].tier != prevTier)
auto const tracker = tr_torrentTracker(fHandle, i);
if (!tier || tier != tracker.tier)
{
[trackers addObject:@{ @"Tier" : @(stats[i].tier + 1), @"Name" : self.name }];
prevTier = stats[i].tier;
tier = tracker.tier;
[trackers addObject:@{ @"Tier" : @(tracker.tier + 1), @"Name" : self.name }];
}
TrackerNode* tracker = [[TrackerNode alloc] initWithTrackerStat:&stats[i] torrent:self];
[trackers addObject:tracker];
auto* tracker_node = [[TrackerNode alloc] initWithTrackerView:&tracker torrent:self];
[trackers addObject:tracker_node];
}
tr_torrentTrackersFree(stats, count);
return trackers;
}
@ -1080,32 +1083,30 @@ bool trashDataFile(char const* filename, tr_error** error)
- (NSUInteger)webSeedCount
{
return fInfo->webseedCount;
return tr_torrentWebseedCount(fHandle);
}
- (NSArray*)webSeeds
{
NSMutableArray* webSeeds = [NSMutableArray arrayWithCapacity:fInfo->webseedCount];
NSUInteger n = tr_torrentWebseedCount(fHandle);
NSMutableArray* webSeeds = [NSMutableArray arrayWithCapacity:n];
double* dlSpeeds = tr_torrentWebSpeeds_KBps(fHandle);
for (NSInteger i = 0; i < fInfo->webseedCount; i++)
for (NSUInteger i = 0; i < n; ++i)
{
auto const webseed = tr_torrentWebseed(fHandle, i);
NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:3];
dict[@"Name"] = self.name;
dict[@"Address"] = @(fInfo->webseeds[i]);
dict[@"Address"] = @(webseed.url);
if (dlSpeeds[i] != -1.0)
if (webseed.is_downloading)
{
dict[@"DL From Rate"] = @(dlSpeeds[i]);
dict[@"DL From Rate"] = @(double(webseed.download_bytes_per_second) / 1000);
}
[webSeeds addObject:dict];
}
tr_free(dlSpeeds);
return webSeeds;
}
@ -1550,7 +1551,7 @@ bool trashDataFile(char const* filename, tr_error** error)
- (NSInteger)fileCount
{
return fInfo->fileCount;
return tr_torrentFileCount(fHandle);
}
- (CGFloat)fileProgress:(FileListNode*)node
@ -1794,21 +1795,19 @@ bool trashDataFile(char const* filename, tr_error** error)
- (NSString*)trackerSortKey
{
int count;
tr_tracker_stat* stats = tr_torrentTrackers(fHandle, &count);
NSString* best = nil;
for (int i = 0; i < count; ++i)
for (size_t i = 0, n = tr_torrentTrackerCount(fHandle); i < n; ++i)
{
NSString* tracker = @(stats[i].host);
if (!best || [tracker localizedCaseInsensitiveCompare:best] == NSOrderedAscending)
auto const tracker = tr_torrentTracker(fHandle, i);
NSString* host = @(tracker.host);
if (!best || [host localizedCaseInsensitiveCompare:best] == NSOrderedAscending)
{
best = tracker;
best = host;
}
}
tr_torrentTrackersFree(stats, count);
return best;
}

View File

@ -30,7 +30,7 @@
@property(nonatomic, weak, readonly) Torrent* torrent;
- (instancetype)initWithTrackerStat:(tr_tracker_stat*)stat torrent:(Torrent*)torrent;
- (instancetype)initWithTrackerView:(tr_tracker_view const*)stat torrent:(Torrent*)torrent;
- (BOOL)isEqual:(id)object;

View File

@ -26,10 +26,10 @@
@implementation TrackerNode
{
tr_tracker_stat fStat;
tr_tracker_view fStat;
}
- (instancetype)initWithTrackerStat:(tr_tracker_stat*)stat torrent:(Torrent*)torrent
- (instancetype)initWithTrackerView:(tr_tracker_view const*)stat torrent:(Torrent*)torrent
{
if ((self = [super init]))
{

View File

@ -35,6 +35,7 @@
#include "ColumnResizer.h"
#include "DetailsDialog.h"
#include "Formatter.h"
#include "IconCache.h"
#include "Prefs.h"
#include "Session.h"
#include "SqueezeLabel.h"
@ -198,18 +199,6 @@ private:
****
***/
QIcon DetailsDialog::getStockIcon(QString const& freedesktop_name, int fallback) const
{
QIcon icon = QIcon::fromTheme(freedesktop_name);
if (icon.isNull())
{
icon = style()->standardIcon(QStyle::StandardPixmap(fallback), nullptr, this);
}
return icon;
}
DetailsDialog::DetailsDialog(Session& session, Prefs& prefs, TorrentModel const& model, QWidget* parent)
: BaseDialog(parent)
, session_(session)
@ -1449,9 +1438,10 @@ void DetailsDialog::initTrackerTab()
ui_.trackersView->setModel(tracker_filter_.get());
ui_.trackersView->setItemDelegate(tracker_delegate_.get());
ui_.addTrackerButton->setIcon(getStockIcon(QStringLiteral("list-add"), QStyle::SP_DialogOpenButton));
ui_.editTrackerButton->setIcon(getStockIcon(QStringLiteral("document-properties"), QStyle::SP_DesktopIcon));
ui_.removeTrackerButton->setIcon(getStockIcon(QStringLiteral("list-remove"), QStyle::SP_TrashIcon));
auto& icons = IconCache::get();
ui_.addTrackerButton->setIcon(icons.getThemeIcon(QStringLiteral("list-add"), QStyle::SP_DialogOpenButton));
ui_.editTrackerButton->setIcon(icons.getThemeIcon(QStringLiteral("document-properties"), QStyle::SP_DesktopIcon));
ui_.removeTrackerButton->setIcon(icons.getThemeIcon(QStringLiteral("list-remove"), QStyle::SP_TrashIcon));
ui_.showTrackerScrapesCheck->setChecked(prefs_.getBool(Prefs::SHOW_TRACKER_SCRAPES));
ui_.showBackupTrackersCheck->setChecked(prefs_.getBool(Prefs::SHOW_BACKUP_TRACKERS));

View File

@ -55,7 +55,6 @@ private:
void initFilesTab() const;
void initOptionsTab();
QIcon getStockIcon(QString const& freedesktop_name, int fallback) const;
void setEnabled(bool);
private slots:

View File

@ -22,6 +22,7 @@
#include "FilterBarComboBox.h"
#include "FilterBarComboBoxDelegate.h"
#include "Filters.h"
#include "IconCache.h"
#include "Prefs.h"
#include "Torrent.h"
#include "TorrentFilter.h"
@ -53,31 +54,33 @@ FilterBarComboBox* FilterBar::createActivityCombo()
model->appendRow(new QStandardItem); // separator
FilterBarComboBoxDelegate::setSeparator(model, model->index(1, 0));
row = new QStandardItem(QIcon::fromTheme(QStringLiteral("system-run")), tr("Active"));
auto& icons = IconCache::get();
row = new QStandardItem(icons.getThemeIcon(QStringLiteral("system-run")), tr("Active"));
row->setData(FilterMode::SHOW_ACTIVE, ACTIVITY_ROLE);
model->appendRow(row);
row = new QStandardItem(QIcon::fromTheme(QStringLiteral("go-down")), tr("Downloading"));
row = new QStandardItem(icons.getThemeIcon(QStringLiteral("go-down")), tr("Downloading"));
row->setData(FilterMode::SHOW_DOWNLOADING, ACTIVITY_ROLE);
model->appendRow(row);
row = new QStandardItem(QIcon::fromTheme(QStringLiteral("go-up")), tr("Seeding"));
row = new QStandardItem(icons.getThemeIcon(QStringLiteral("go-up")), tr("Seeding"));
row->setData(FilterMode::SHOW_SEEDING, ACTIVITY_ROLE);
model->appendRow(row);
row = new QStandardItem(QIcon::fromTheme(QStringLiteral("media-playback-pause")), tr("Paused"));
row = new QStandardItem(icons.getThemeIcon(QStringLiteral("media-playback-pause")), tr("Paused"));
row->setData(FilterMode::SHOW_PAUSED, ACTIVITY_ROLE);
model->appendRow(row);
row = new QStandardItem(QIcon::fromTheme(QStringLiteral("dialog-ok")), tr("Finished"));
row = new QStandardItem(icons.getThemeIcon(QStringLiteral("dialog-ok")), tr("Finished"));
row->setData(FilterMode::SHOW_FINISHED, ACTIVITY_ROLE);
model->appendRow(row);
row = new QStandardItem(QIcon::fromTheme(QStringLiteral("view-refresh")), tr("Verifying"));
row = new QStandardItem(icons.getThemeIcon(QStringLiteral("view-refresh")), tr("Verifying"));
row->setData(FilterMode::SHOW_VERIFYING, ACTIVITY_ROLE);
model->appendRow(row);
row = new QStandardItem(QIcon::fromTheme(QStringLiteral("process-stop")), tr("Error"));
row = new QStandardItem(icons.getThemeIcon(QStringLiteral("process-stop")), tr("Error"));
row->setData(FilterMode::SHOW_ERROR, ACTIVITY_ROLE);
model->appendRow(row);

View File

@ -13,6 +13,7 @@
#include <shellapi.h>
#endif
#include <QApplication>
#include <QFile>
#include <QFileIconProvider>
#include <QFileInfo>
@ -86,11 +87,11 @@ QIcon IconCache::getMimeTypeIcon(QString const& mime_type_name, bool multifile)
{
QMimeDatabase mime_db;
auto const type = mime_db.mimeTypeForName(mime_type_name);
icon = QIcon::fromTheme(type.iconName());
icon = getThemeIcon(type.iconName());
if (icon.isNull())
{
icon = QIcon::fromTheme(type.genericIconName());
icon = getThemeIcon(type.genericIconName());
}
if (icon.isNull())
@ -125,6 +126,11 @@ QIcon IconCache::getMimeTypeIcon(QString const& mime_type_name, bool multifile)
return icon;
}
QIcon IconCache::getThemeIcon(QString const& name, std::optional<QStyle::StandardPixmap> const& fallback) const
{
return getThemeIcon(name, name + QStringLiteral("-symbolic"), fallback);
}
/***
****
***/
@ -197,12 +203,12 @@ QIcon IconCache::getMimeIcon(QString const& filename) const
auto const type = mime_db.mimeTypeForFile(filename, QMimeDatabase::MatchExtension);
if (icon.isNull())
{
icon = QIcon::fromTheme(type.iconName());
icon = getThemeIcon(type.iconName());
}
if (icon.isNull())
{
icon = QIcon::fromTheme(type.genericIconName());
icon = getThemeIcon(type.genericIconName());
}
if (icon.isNull())
@ -215,3 +221,25 @@ QIcon IconCache::getMimeIcon(QString const& filename) const
}
#endif
QIcon IconCache::getThemeIcon(
QString const& name,
QString const& fallbackName,
std::optional<QStyle::StandardPixmap> const& fallbackPixmap) const
{
static auto const rtlSuffix = qApp->layoutDirection() == Qt::RightToLeft ? QStringLiteral("-rtl") : QString();
auto icon = QIcon::fromTheme(name + rtlSuffix);
if (icon.isNull())
{
icon = getThemeIcon(fallbackName + rtlSuffix);
}
if (icon.isNull() && fallbackPixmap.has_value())
{
icon = qApp->style()->standardIcon(*fallbackPixmap, nullptr);
}
return icon;
}

View File

@ -12,11 +12,13 @@
#include <unordered_set>
#endif
#include <optional>
#include <unordered_map>
#include <QFileIconProvider>
#include <QIcon>
#include <QString>
#include <QStyle>
#include "Utils.h" // std::hash<QString>()
@ -44,6 +46,8 @@ public:
QIcon guessMimeIcon(QString const& filename, QIcon fallback = {}) const;
QIcon getMimeTypeIcon(QString const& mime_type, bool multifile) const;
QIcon getThemeIcon(QString const& name, std::optional<QStyle::StandardPixmap> const& fallback = {}) const;
protected:
IconCache() = default;
@ -61,4 +65,9 @@ private:
mutable std::unordered_map<QString, QIcon> ext_to_icon_;
QIcon getMimeIcon(QString const& filename) const;
#endif
QIcon getThemeIcon(
QString const& name,
QString const& fallbackName,
std::optional<QStyle::StandardPixmap> const& fallbackPixmap) const;
};

View File

@ -30,6 +30,7 @@
#include "FilterBar.h"
#include "Filters.h"
#include "Formatter.h"
#include "IconCache.h"
#include "MainWindow.h"
#include "MakeDialog.h"
#include "OptionsDialog.h"
@ -78,18 +79,6 @@ public:
}
};
QIcon MainWindow::getStockIcon(QString const& name, int fallback) const
{
QIcon icon = QIcon::fromTheme(name);
if (icon.isNull() && fallback >= 0)
{
icon = style()->standardIcon(QStyle::StandardPixmap(fallback), nullptr, this);
}
return icon;
}
QIcon MainWindow::addEmblem(QIcon base_icon, QStringList const& emblem_names) const
{
if (base_icon.isNull())
@ -97,11 +86,12 @@ QIcon MainWindow::addEmblem(QIcon base_icon, QStringList const& emblem_names) co
return base_icon;
}
auto& icons = IconCache::get();
QIcon emblem_icon;
for (QString const& emblem_name : emblem_names)
{
emblem_icon = QIcon::fromTheme(emblem_name);
emblem_icon = icons.getThemeIcon(emblem_name);
if (!emblem_icon.isNull())
{
@ -156,41 +146,43 @@ MainWindow::MainWindow(Session& session, Prefs& prefs, TorrentModel& model, bool
ui_.listView->setStyle(lvp_style_.get());
ui_.listView->setAttribute(Qt::WA_MacShowFocusRect, false);
auto& icons = IconCache::get();
// icons
QIcon const icon_play = getStockIcon(QStringLiteral("media-playback-start"), QStyle::SP_MediaPlay);
QIcon const icon_pause = getStockIcon(QStringLiteral("media-playback-pause"), QStyle::SP_MediaPause);
QIcon const icon_open = getStockIcon(QStringLiteral("document-open"), QStyle::SP_DialogOpenButton);
QIcon const icon_play = icons.getThemeIcon(QStringLiteral("media-playback-start"), QStyle::SP_MediaPlay);
QIcon const icon_pause = icons.getThemeIcon(QStringLiteral("media-playback-pause"), QStyle::SP_MediaPause);
QIcon const icon_open = icons.getThemeIcon(QStringLiteral("document-open"), QStyle::SP_DialogOpenButton);
ui_.action_OpenFile->setIcon(icon_open);
ui_.action_AddURL->setIcon(
addEmblem(icon_open, QStringList() << QStringLiteral("emblem-web") << QStringLiteral("applications-internet")));
ui_.action_New->setIcon(getStockIcon(QStringLiteral("document-new"), QStyle::SP_DesktopIcon));
ui_.action_Properties->setIcon(getStockIcon(QStringLiteral("document-properties"), QStyle::SP_DesktopIcon));
ui_.action_OpenFolder->setIcon(getStockIcon(QStringLiteral("folder-open"), QStyle::SP_DirOpenIcon));
ui_.action_New->setIcon(icons.getThemeIcon(QStringLiteral("document-new"), QStyle::SP_DesktopIcon));
ui_.action_Properties->setIcon(icons.getThemeIcon(QStringLiteral("document-properties"), QStyle::SP_DesktopIcon));
ui_.action_OpenFolder->setIcon(icons.getThemeIcon(QStringLiteral("folder-open"), QStyle::SP_DirOpenIcon));
ui_.action_Start->setIcon(icon_play);
ui_.action_StartNow->setIcon(icon_play);
ui_.action_Announce->setIcon(getStockIcon(QStringLiteral("network-transmit-receive")));
ui_.action_Announce->setIcon(icons.getThemeIcon(QStringLiteral("network-transmit-receive")));
ui_.action_Pause->setIcon(icon_pause);
ui_.action_Remove->setIcon(getStockIcon(QStringLiteral("list-remove"), QStyle::SP_TrashIcon));
ui_.action_Delete->setIcon(getStockIcon(QStringLiteral("edit-delete"), QStyle::SP_TrashIcon));
ui_.action_Remove->setIcon(icons.getThemeIcon(QStringLiteral("list-remove"), QStyle::SP_TrashIcon));
ui_.action_Delete->setIcon(icons.getThemeIcon(QStringLiteral("edit-delete"), QStyle::SP_TrashIcon));
ui_.action_StartAll->setIcon(icon_play);
ui_.action_PauseAll->setIcon(icon_pause);
ui_.action_Quit->setIcon(getStockIcon(QStringLiteral("application-exit")));
ui_.action_SelectAll->setIcon(getStockIcon(QStringLiteral("edit-select-all")));
ui_.action_ReverseSortOrder->setIcon(getStockIcon(QStringLiteral("view-sort-ascending"), QStyle::SP_ArrowDown));
ui_.action_Preferences->setIcon(getStockIcon(QStringLiteral("preferences-system")));
ui_.action_Contents->setIcon(getStockIcon(QStringLiteral("help-contents"), QStyle::SP_DialogHelpButton));
ui_.action_About->setIcon(getStockIcon(QStringLiteral("help-about")));
ui_.action_QueueMoveTop->setIcon(getStockIcon(QStringLiteral("go-top")));
ui_.action_QueueMoveUp->setIcon(getStockIcon(QStringLiteral("go-up"), QStyle::SP_ArrowUp));
ui_.action_QueueMoveDown->setIcon(getStockIcon(QStringLiteral("go-down"), QStyle::SP_ArrowDown));
ui_.action_QueueMoveBottom->setIcon(getStockIcon(QStringLiteral("go-bottom")));
ui_.action_Quit->setIcon(icons.getThemeIcon(QStringLiteral("application-exit")));
ui_.action_SelectAll->setIcon(icons.getThemeIcon(QStringLiteral("edit-select-all")));
ui_.action_ReverseSortOrder->setIcon(icons.getThemeIcon(QStringLiteral("view-sort-ascending"), QStyle::SP_ArrowDown));
ui_.action_Preferences->setIcon(icons.getThemeIcon(QStringLiteral("preferences-system")));
ui_.action_Contents->setIcon(icons.getThemeIcon(QStringLiteral("help-contents"), QStyle::SP_DialogHelpButton));
ui_.action_About->setIcon(icons.getThemeIcon(QStringLiteral("help-about")));
ui_.action_QueueMoveTop->setIcon(icons.getThemeIcon(QStringLiteral("go-top")));
ui_.action_QueueMoveUp->setIcon(icons.getThemeIcon(QStringLiteral("go-up"), QStyle::SP_ArrowUp));
ui_.action_QueueMoveDown->setIcon(icons.getThemeIcon(QStringLiteral("go-down"), QStyle::SP_ArrowDown));
ui_.action_QueueMoveBottom->setIcon(icons.getThemeIcon(QStringLiteral("go-bottom")));
ui_.optionsButton->setIcon(getStockIcon(QStringLiteral("preferences-other")));
ui_.statsModeButton->setIcon(getStockIcon(QStringLiteral("view-statistics")));
ui_.optionsButton->setIcon(icons.getThemeIcon(QStringLiteral("preferences-other")));
ui_.statsModeButton->setIcon(icons.getThemeIcon(QStringLiteral("view-statistics")));
auto make_network_pixmap = [this](QString name, QSize size = { 16, 16 })
auto make_network_pixmap = [this, &icons](QString name, QSize size = { 16, 16 })
{
return getStockIcon(name, QStyle::SP_DriveNetIcon).pixmap(size);
return icons.getThemeIcon(name, QStyle::SP_DriveNetIcon).pixmap(size);
};
pixmap_network_error_ = make_network_pixmap(QStringLiteral("network-error"));
pixmap_network_idle_ = make_network_pixmap(QStringLiteral("network-idle"));

View File

@ -123,7 +123,6 @@ private slots:
void trayActivated(QSystemTrayIcon::ActivationReason);
private:
QIcon getStockIcon(QString const&, int fallback = -1) const;
QIcon addEmblem(QIcon icon, QStringList const& emblem_names) const;
torrent_ids_t getSelectedTorrents(bool withMetadataOnly = false) const;

View File

@ -17,6 +17,7 @@
#include <QStyleOptionProgressBar>
#include "Formatter.h"
#include "IconCache.h"
#include "Torrent.h"
#include "TorrentDelegate.h"
#include "TorrentModel.h"
@ -437,12 +438,7 @@ QIcon& TorrentDelegate::getWarningEmblem() const
if (icon.isNull())
{
icon = QIcon::fromTheme(QStringLiteral("emblem-important"));
}
if (icon.isNull())
{
icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning);
icon = IconCache::get().getThemeIcon(QStringLiteral("emblem-important"), QStyle::SP_MessageBoxWarning);
}
return icon;

View File

@ -16,6 +16,7 @@ function global:Build-Qt([string] $PrefixDir, [string] $Arch, [string] $DepsPref
$UnpackFlags = @(
(Join-Path $ArchiveBase qtactiveqt '*')
(Join-Path $ArchiveBase qtbase '*')
(Join-Path $ArchiveBase qtsvg '*')
(Join-Path $ArchiveBase qttools '*')
(Join-Path $ArchiveBase qttranslations '*')
(Join-Path $ArchiveBase qtwinextras '*')

View File

@ -36,13 +36,21 @@ function global:Build-Transmission([string] $PrefixDir, [string] $Arch, [string]
Copy-Item -Path (Join-Path $DepsPrefixDir bin "${x}.pdb") -Destination $DebugSymbolsDir
}
foreach ($x in @('Core', 'DBus', 'Gui', 'Network', 'Widgets', 'WinExtras')) {
foreach ($x in @('Core', 'DBus', 'Gui', 'Network', 'Svg', 'Widgets', 'WinExtras')) {
if ($DepsPrefixDir -ne $PrefixDir) {
Copy-Item -Path (Join-Path $DepsPrefixDir bin "Qt5${x}.dll") -Destination (Join-Path $PrefixDir bin)
}
Copy-Item -Path (Join-Path $DepsPrefixDir bin "Qt5${x}.pdb") -Destination $DebugSymbolsDir
}
foreach ($x in @('gif', 'ico', 'jpeg', 'svg')) {
if ($DepsPrefixDir -ne $PrefixDir) {
New-Item -Path (Join-Path $PrefixDir plugins imageformats) -ItemType Directory -ErrorAction Ignore | Out-Null
Copy-Item -Path (Join-Path $DepsPrefixDir plugins imageformats "q${x}.dll") -Destination (Join-Path $PrefixDir plugins imageformats)
}
Copy-Item -Path (Join-Path $DepsPrefixDir plugins imageformats "q${x}.pdb") -Destination $DebugSymbolsDir
}
if ($DepsPrefixDir -ne $PrefixDir) {
New-Item -Path (Join-Path $PrefixDir plugins platforms) -ItemType Directory -ErrorAction Ignore | Out-Null
Copy-Item -Path (Join-Path $DepsPrefixDir plugins platforms qwindows.dll) -Destination (Join-Path $PrefixDir plugins platforms)