mirror of
https://github.com/transmission/transmission
synced 2025-02-20 21:26:53 +00:00
Merge branch 'master' into patch-1
This commit is contained in:
commit
ef7ff98f03
32 changed files with 521 additions and 573 deletions
12
dist/msi/components/QtClient.wxs
vendored
12
dist/msi/components/QtClient.wxs
vendored
|
@ -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 ?>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/***
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
||||
/***
|
||||
|
|
|
@ -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);
|
||||
|
||||
/***
|
||||
****
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
***********************************************************************
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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]))
|
||||
{
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -55,7 +55,6 @@ private:
|
|||
void initFilesTab() const;
|
||||
void initOptionsTab();
|
||||
|
||||
QIcon getStockIcon(QString const& freedesktop_name, int fallback) const;
|
||||
void setEnabled(bool);
|
||||
|
||||
private slots:
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 '*')
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue