refactor: use fmt to build the GTK client's user-visible strings (#2788)

Co-authored-by: Mike Gelfand <mikedld@mikedld.com>
This commit is contained in:
Charles Kerr 2022-03-21 09:15:48 -05:00 committed by GitHub
parent 44e30bf092
commit fcc1510ecb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 512 additions and 437 deletions

View File

@ -107,7 +107,7 @@ static auto constexpr Options = std::array<tr_option, 45>{
{ { 'a', "allowed", "Allowed IP addresses. (Default: " TR_DEFAULT_RPC_WHITELIST ")", "a", true, "<list>" }, { { 'a', "allowed", "Allowed IP addresses. (Default: " TR_DEFAULT_RPC_WHITELIST ")", "a", true, "<list>" },
{ 'b', "blocklist", "Enable peer blocklists", "b", false, nullptr }, { 'b', "blocklist", "Enable peer blocklists", "b", false, nullptr },
{ 'B', "no-blocklist", "Disable peer blocklists", "B", false, nullptr }, { 'B', "no-blocklist", "Disable peer blocklists", "B", false, nullptr },
{ 'c', "watch-dir", "Where to watch for new .torrent files", "c", true, "<directory>" }, { 'c', "watch-dir", "Where to watch for new torrent files", "c", true, "<directory>" },
{ 'C', "no-watch-dir", "Disable the watch-dir", "C", false, nullptr }, { 'C', "no-watch-dir", "Disable the watch-dir", "C", false, nullptr },
{ 941, "incomplete-dir", "Where to store new torrents until they're complete", nullptr, true, "<directory>" }, { 941, "incomplete-dir", "Where to store new torrents until they're complete", nullptr, true, "<directory>" },
{ 942, "no-incomplete-dir", "Don't store incomplete torrents in a different location", nullptr, false, nullptr }, { 942, "no-incomplete-dir", "Don't store incomplete torrents in a different location", nullptr, false, nullptr },
@ -243,7 +243,7 @@ static auto onFileAdded(tr_watchdir_t dir, char const* name, void* vsession)
if (tr_torrentNew(ctor, nullptr) == nullptr) if (tr_torrentNew(ctor, nullptr) == nullptr)
{ {
tr_logAddError(fmt::format(_("Couldn't add .torrent file '{path}'"), fmt::arg("path", name))); tr_logAddError(fmt::format(_("Couldn't add torrent file '{path}'"), fmt::arg("path", name)));
} }
else else
{ {
@ -254,7 +254,7 @@ static auto onFileAdded(tr_watchdir_t dir, char const* name, void* vsession)
{ {
tr_error* error = nullptr; tr_error* error = nullptr;
tr_logAddInfo(fmt::format(_("Removing .torrent file '{path}'"), fmt::arg("path", name))); tr_logAddInfo(fmt::format(_("Removing torrent file '{path}'"), fmt::arg("path", name)));
if (!tr_sys_path_remove(filename.c_str(), &error)) if (!tr_sys_path_remove(filename.c_str(), &error))
{ {
@ -761,7 +761,7 @@ static int daemon_start(void* varg, [[maybe_unused]] bool foreground)
(void)tr_variantDictFindStrView(settings, TR_KEY_watch_dir, &dir); (void)tr_variantDictFindStrView(settings, TR_KEY_watch_dir, &dir);
if (!std::empty(dir)) if (!std::empty(dir))
{ {
tr_logAddInfo(fmt::format(_("Watching '{path}' for new .torrent files"), fmt::arg("path", dir))); tr_logAddInfo(fmt::format(_("Watching '{path}' for new torrent files"), fmt::arg("path", dir)));
watchdir = tr_watchdir_new(dir, &onFileAdded, mySession, ev_base, force_generic); watchdir = tr_watchdir_new(dir, &onFileAdded, mySession, ev_base, force_generic);
if (watchdir == nullptr) if (watchdir == nullptr)

View File

@ -351,7 +351,7 @@ void register_magnet_link_handler()
auto const msg = fmt::format( auto const msg = fmt::format(
_("Couldn't register Transmission as a {content_type} handler: {error} ({error_code})"), _("Couldn't register Transmission as a {content_type} handler: {error} ({error_code})"),
fmt::arg("content_type", content_type), fmt::arg("content_type", content_type),
fmt::arg("error", e.what().raw()), fmt::arg("error", e.what()),
fmt::arg("error_code", e.code())); fmt::arg("error_code", e.code()));
g_warning("%s", msg.c_str()); g_warning("%s", msg.c_str());
} }

View File

@ -17,6 +17,10 @@
#include <glibmm/i18n.h> #include <glibmm/i18n.h>
#include <fmt/core.h>
#include <fmt/chrono.h>
#include <fmt/format.h>
#include <libtransmission/transmission.h> #include <libtransmission/transmission.h>
#include <libtransmission/utils.h> /* tr_free */ #include <libtransmission/utils.h> /* tr_free */
#include <libtransmission/web-utils.h> #include <libtransmission/web-utils.h>
@ -464,7 +468,7 @@ Gtk::Widget* DetailsDialog::Impl::options_page_new()
[this]() { torrent_set_bool(TR_KEY_honorsSessionLimits, honor_limits_check_->get_active()); }); [this]() { torrent_set_bool(TR_KEY_honorsSessionLimits, honor_limits_check_->get_active()); });
down_limited_check_ = Gtk::make_managed<Gtk::CheckButton>( down_limited_check_ = Gtk::make_managed<Gtk::CheckButton>(
gtr_sprintf(_("Limit _download speed (%s):"), _(speed_K_str)), fmt::format(_("Limit _download speed ({speed_units}):"), fmt::arg("speed_units", speed_K_str)),
true); true);
down_limited_check_->set_active(false); down_limited_check_->set_active(false);
down_limited_check_tag_ = down_limited_check_->signal_toggled().connect( down_limited_check_tag_ = down_limited_check_->signal_toggled().connect(
@ -475,7 +479,9 @@ Gtk::Widget* DetailsDialog::Impl::options_page_new()
[this]() { torrent_set_int(TR_KEY_downloadLimit, down_limit_spin_->get_value_as_int()); }); [this]() { torrent_set_int(TR_KEY_downloadLimit, down_limit_spin_->get_value_as_int()); });
t->add_row_w(row, *down_limited_check_, *down_limit_spin_); t->add_row_w(row, *down_limited_check_, *down_limit_spin_);
up_limited_check_ = Gtk::make_managed<Gtk::CheckButton>(gtr_sprintf(_("Limit _upload speed (%s):"), _(speed_K_str)), true); up_limited_check_ = Gtk::make_managed<Gtk::CheckButton>(
fmt::format(_("Limit _upload speed ({speed_units}):"), fmt::arg("speed_units", speed_K_str)),
true);
up_limited_check_tag_ = up_limited_check_->signal_toggled().connect( up_limited_check_tag_ = up_limited_check_->signal_toggled().connect(
[this]() { torrent_set_bool(TR_KEY_uploadLimited, up_limited_check_->get_active()); }); [this]() { torrent_set_bool(TR_KEY_uploadLimited, up_limited_check_->get_active()); });
@ -593,19 +599,16 @@ void gtr_text_buffer_set_text(Glib::RefPtr<Gtk::TextBuffer> const& b, Glib::ustr
} }
} }
Glib::ustring get_short_date_string(time_t t) std::string get_date_string(time_t t)
{ {
char buf[64];
struct tm tm;
if (t == 0) if (t == 0)
{ {
return _("N/A"); return _("N/A");
} }
struct tm tm;
tr_localtime_r(&t, &tm); tr_localtime_r(&t, &tm);
strftime(buf, sizeof(buf), "%d %b %Y", &tm); return fmt::format("{:%x}", tm);
return Glib::locale_to_utf8(buf);
} }
} // namespace } // namespace
@ -660,9 +663,9 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
} }
else else
{ {
auto const creator = Glib::ustring(infos.front().creator != nullptr ? infos.front().creator : ""); auto const creator = tr_strvStrip(infos.front().creator != nullptr ? infos.front().creator : "");
auto const date = infos.front().date_created; auto const date = infos.front().date_created;
auto const datestr = get_short_date_string(date); auto const datestr = get_date_string(date);
bool const mixed_creator = std::any_of( bool const mixed_creator = std::any_of(
infos.begin(), infos.begin(),
infos.end(), infos.end(),
@ -672,31 +675,28 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
infos.end(), infos.end(),
[date](auto const& info) { return date != info.date_created; }); [date](auto const& info) { return date != info.date_created; });
bool const empty_creator = creator.empty(); bool const empty_creator = std::empty(creator);
bool const empty_date = date == 0; bool const empty_date = date == 0;
if (mixed_date || mixed_creator) if (mixed_creator || mixed_date)
{ {
str = mixed; str = mixed;
} }
else if (empty_date && empty_creator) else if (!empty_creator && !empty_date)
{ {
str = _("N/A"); str = fmt::format(_("Created by {creator} on {date}"), fmt::arg("creator", creator), fmt::arg("date", datestr));
}
else if (!empty_creator)
{
str = fmt::format(_("Created by {creator}"), fmt::arg("creator", creator));
}
else if (!empty_date)
{
str = fmt::format(_("Created on {date}"), fmt::arg("date", datestr));
} }
else else
{ {
if (empty_date && !empty_creator) str = _("N/A");
{
str = gtr_sprintf(_("Created by %1$s"), creator);
}
else if (empty_creator && !empty_date)
{
str = gtr_sprintf(_("Created on %1$s"), datestr);
}
else
{
str = gtr_sprintf(_("Created by %1$s on %2$s"), creator, datestr);
}
} }
} }
@ -845,15 +845,21 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
} }
else if (pieceSize >= 0) else if (pieceSize >= 0)
{ {
str = gtr_sprintf( str = fmt::format(
ngettext("%1$s (%2$'d piece @ %3$s)", "%1$s (%2$'d pieces @ %3$s)", pieces), ngettext(
sizebuf, "{file_size} ({piece_count} piece @ {piece_size})",
pieces, "{file_size} ({piece_count} pieces @ {piece_size})",
tr_formatter_mem_B(pieceSize)); pieces),
fmt::arg("file_size", sizebuf),
fmt::arg("piece_count", pieces),
fmt::arg("piece_size", tr_formatter_mem_B(pieceSize)));
} }
else else
{ {
str = gtr_sprintf(ngettext("%1$s (%2$'d piece)", "%1$s (%2$'d pieces)", pieces), sizebuf, pieces); str = fmt::format(
ngettext("{file_size} ({piece_count} piece)", "{file_size} ({piece_count} pieces)", pieces),
fmt::arg("file_size", sizebuf),
fmt::arg("piece_count", pieces));
} }
size_lb_->set_text(str); size_lb_->set_text(str);
@ -891,47 +897,62 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
if (haveUnchecked == 0 && leftUntilDone == 0) if (haveUnchecked == 0 && leftUntilDone == 0)
{ {
str = gtr_sprintf(_("%1$s (%2$s%%)"), total, buf2); str = fmt::format(
_("{current_size} ({percent_done}%)"),
fmt::arg("current_size", total),
fmt::arg("percent_done", buf2));
} }
else if (haveUnchecked == 0) else if (haveUnchecked == 0)
{ {
str = gtr_sprintf(_("%1$s (%2$s%% of %3$s%% Available)"), total, buf2, avail); str = fmt::format(
_("{current_size} ({percent_done}% of {percent_available}% available"),
fmt::arg("current_size", total),
fmt::arg("percent_done", buf2),
fmt::arg("percent_available", avail));
} }
else else
{ {
str = gtr_sprintf(_("%1$s (%2$s%% of %3$s%% Available); %4$s Unverified"), total, buf2, avail, unver); str = fmt::format(
_("{current_size} ({percent_done}% of {percent_available}% available; {unverified_size} unverified)"),
fmt::arg("current_size", total),
fmt::arg("percent_done", buf2),
fmt::arg("percent_available", avail),
fmt::arg("unverified_size", unver));
} }
} }
} }
have_lb_->set_text(str); have_lb_->set_text(str);
/* dl_lb */ // dl_lb
if (stats.empty()) if (stats.empty())
{ {
str = no_torrent; str = no_torrent;
} }
else else
{ {
uint64_t d = 0; auto const downloaded_str = tr_strlsize(std::accumulate(
uint64_t f = 0; std::begin(stats),
std::end(stats),
uint64_t{ 0 },
[](auto sum, auto const* st) { return sum + st->downloadedEver; }));
for (auto const* const st : stats) auto const failed = std::accumulate(
std::begin(stats),
std::end(stats),
uint64_t{ 0 },
[](auto sum, auto const* st) { return sum + st->corruptEver; });
if (failed != 0)
{ {
d += st->downloadedEver; str = fmt::format(
f += st->corruptEver; _("{downloaded_size} (+{discarded_size} discarded after failed checksum)"),
} fmt::arg("downloaded_size", downloaded_str),
fmt::arg("discarded_size", tr_strlsize(failed)));
auto const dbuf = tr_strlsize(d);
auto const fbuf = tr_strlsize(f);
if (f != 0)
{
str = gtr_sprintf(_("%1$s (+%2$s discarded after failed checksum)"), dbuf, fbuf);
} }
else else
{ {
str = dbuf; str = downloaded_str;
} }
} }
@ -954,7 +975,10 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
std::end(stats), std::end(stats),
uint64_t{}, uint64_t{},
[](auto sum, auto const* st) { return sum + st->sizeWhenDone; }); [](auto sum, auto const* st) { return sum + st->sizeWhenDone; });
str = gtr_sprintf(_("%s (Ratio: %s)"), tr_strlsize(uploaded), tr_strlratio(tr_getRatio(uploaded, denominator))); str = fmt::format(
_("{uploaded_size} (Ratio: {ratio})"),
fmt::arg("uploaded_size", tr_strlsize(uploaded)),
fmt::arg("ratio", tr_strlratio(tr_getRatio(uploaded, denominator))));
} }
ul_lb_->set_text(str); ul_lb_->set_text(str);
@ -1025,7 +1049,8 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
} }
else else
{ {
str = gtr_sprintf(_("%1$s ago"), tr_strltime(period)); // e.g. 5 minutes ago
str = fmt::format(_("{time_span} ago"), fmt::arg("time_span", tr_strltime(period)));
} }
} }
} }
@ -1355,6 +1380,11 @@ void DetailsDialog::Impl::refreshPeerList(std::vector<tr_torrent*> const& torren
row[peer_cols.was_updated] = false; row[peer_cols.was_updated] = false;
} }
auto make_key = [](tr_torrent const* tor, tr_peer_stat const* ps)
{
return fmt::format("{}.{}", tr_torrentId(tor), ps->addr);
};
/* step 3: add any new peers */ /* step 3: add any new peers */
for (size_t i = 0; i < torrents.size(); ++i) for (size_t i = 0; i < torrents.size(); ++i)
{ {
@ -1363,7 +1393,7 @@ void DetailsDialog::Impl::refreshPeerList(std::vector<tr_torrent*> const& torren
for (int j = 0; j < peerCount[i]; ++j) for (int j = 0; j < peerCount[i]; ++j)
{ {
auto const* s = &peers.at(i)[j]; auto const* s = &peers.at(i)[j];
auto const key = gtr_sprintf("%d.%s", tr_torrentId(tor), s->addr); auto const key = make_key(tor, s);
if (hash.find(key) == hash.end()) if (hash.find(key) == hash.end())
{ {
@ -1382,7 +1412,7 @@ void DetailsDialog::Impl::refreshPeerList(std::vector<tr_torrent*> const& torren
for (int j = 0; j < peerCount[i]; ++j) for (int j = 0; j < peerCount[i]; ++j)
{ {
auto const* s = &peers.at(i)[j]; auto const* s = &peers.at(i)[j];
auto const key = gtr_sprintf("%d.%s", tr_torrentId(tor), s->addr); auto const key = make_key(tor, s);
refreshPeerRow(store->get_iter(hash.at(key).get_path()), s); refreshPeerRow(store->get_iter(hash.at(key).get_path()), s);
} }
} }
@ -1418,6 +1448,11 @@ void DetailsDialog::Impl::refreshWebseedList(std::vector<tr_torrent*> const& tor
auto& hash = webseed_hash_; auto& hash = webseed_hash_;
auto& store = webseed_store_; auto& store = webseed_store_;
auto make_key = [](tr_torrent const* tor, char const* url)
{
return fmt::format("{}.{}", tr_torrentId(tor), url);
};
/* step 1: mark all webseeds as not-updated */ /* step 1: mark all webseeds as not-updated */
for (auto const& row : store->children()) for (auto const& row : store->children())
{ {
@ -1432,7 +1467,7 @@ void DetailsDialog::Impl::refreshWebseedList(std::vector<tr_torrent*> const& tor
has_any_webseeds = true; has_any_webseeds = true;
auto const* const url = tr_torrentWebseed(tor, j).url; auto const* const url = tr_torrentWebseed(tor, j).url;
auto const key = gtr_sprintf("%d.%s", tr_torrentId(tor), url); auto const key = make_key(tor, url);
if (hash.find(key) == hash.end()) if (hash.find(key) == hash.end())
{ {
@ -1450,7 +1485,7 @@ void DetailsDialog::Impl::refreshWebseedList(std::vector<tr_torrent*> const& tor
for (size_t j = 0, n = tr_torrentWebseedCount(tor); j < n; ++j) for (size_t j = 0, n = tr_torrentWebseedCount(tor); j < n; ++j)
{ {
auto const webseed = tr_torrentWebseed(tor, j); auto const webseed = tr_torrentWebseed(tor, j);
auto const key = gtr_sprintf("%d.%s", tr_torrentId(tor), webseed.url); auto const key = make_key(tor, webseed.url);
auto const iter = store->get_iter(hash.at(key).get_path()); auto const iter = store->get_iter(hash.at(key).get_path());
auto const KBps = double(webseed.download_bytes_per_second) / speed_K; auto const KBps = double(webseed.download_bytes_per_second) / speed_K;
@ -1649,6 +1684,7 @@ void setPeerViewColumns(Gtk::TreeView* peer_view)
else if (*col == peer_cols.progress) else if (*col == peer_cols.progress)
{ {
auto* r = Gtk::make_managed<Gtk::CellRendererProgress>(); auto* r = Gtk::make_managed<Gtk::CellRendererProgress>();
// % is percent done
c = Gtk::make_managed<Gtk::TreeViewColumn>(_("%"), *r); c = Gtk::make_managed<Gtk::TreeViewColumn>(_("%"), *r);
c->add_attribute(r->property_text(), *col); c->add_attribute(r->property_text(), *col);
} }
@ -1837,12 +1873,12 @@ Gtk::Widget* DetailsDialog::Impl::peer_page_new()
namespace namespace
{ {
char const err_markup_begin[] = "<span color=\"red\">"; auto constexpr ErrMarkupBegin = "<span color=\"red\">"sv;
char const err_markup_end[] = "</span>"; auto constexpr ErrMarkupEnd = "</span>"sv;
char const timeout_markup_begin[] = "<span color=\"#246\">"; auto constexpr TimeoutMarkupBegin = "<span color=\"#246\">"sv;
char const timeout_markup_end[] = "</span>"; auto constexpr TimeoutMarkupEnd = "</span>"sv;
char const success_markup_begin[] = "<span color=\"#080\">"; auto constexpr SuccessMarkupBegin = "<span color=\"#080\">"sv;
char const success_markup_end[] = "</span>"; auto constexpr SuccessMarkupEnd = "</span>"sv;
std::array<std::string_view, 3> const text_dir_mark = { ""sv, "\u200E"sv, "\u200F"sv }; std::array<std::string_view, 3> const text_dir_mark = { ""sv, "\u200E"sv, "\u200F"sv };
@ -1867,29 +1903,35 @@ void appendAnnounceInfo(tr_tracker_view const& tracker, time_t const now, Gtk::T
if (tracker.lastAnnounceSucceeded) if (tracker.lastAnnounceSucceeded)
{ {
gstr << gtr_sprintf( gstr << fmt::format(
_("Got a list of %1$s%2$'d peers%3$s %4$s ago"), // {markup_begin} and {markup_end} should surround the peer text
success_markup_begin, ngettext(
tracker.lastAnnouncePeerCount, "Got a list of {markup_begin}{peer_count} peer{markup_end} {time_span} ago",
success_markup_end, "Got a list of {markup_begin}{peer_count} peers{markup_end} {time_span} ago",
timebuf); tracker.lastAnnouncePeerCount),
fmt::arg("markup_begin", SuccessMarkupBegin),
fmt::arg("peer_count", tracker.lastAnnouncePeerCount),
fmt::arg("markup_end", SuccessMarkupEnd),
fmt::arg("time_span", timebuf));
} }
else if (tracker.lastAnnounceTimedOut) else if (tracker.lastAnnounceTimedOut)
{ {
gstr << gtr_sprintf( gstr << fmt::format(
_("Peer list request %1$stimed out%2$s %3$s ago; will retry"), // {markup_begin} and {markup_end} should surround the time_span
timeout_markup_begin, _("Peer list request {markup_begin}timed out {time_span} ago{markup_end}; will retry"),
timeout_markup_end, fmt::arg("markup_begin", TimeoutMarkupBegin),
timebuf); fmt::arg("time_span", timebuf),
fmt::arg("markup_end", TimeoutMarkupEnd));
} }
else else
{ {
gstr << gtr_sprintf( gstr << fmt::format(
_("Got an error %1$s\"%2$s\"%3$s %4$s ago"), // {markup_begin} and {markup_end} should surround the error
err_markup_begin, _("Got an error '{markup_begin}{error}{markup_end}' {time_span} ago"),
tracker.lastAnnounceResult, fmt::arg("markup_begin", ErrMarkupBegin),
err_markup_end, fmt::arg("error", Glib::Markup::escape_text(tracker.lastAnnounceResult)),
timebuf); fmt::arg("markup_end", ErrMarkupEnd),
fmt::arg("time_span", timebuf));
} }
} }
@ -1904,7 +1946,9 @@ void appendAnnounceInfo(tr_tracker_view const& tracker, time_t const now, Gtk::T
case TR_TRACKER_WAITING: case TR_TRACKER_WAITING:
gstr << '\n'; gstr << '\n';
gstr << text_dir_mark[direction]; gstr << text_dir_mark[direction];
gstr << gtr_sprintf(_("Asking for more peers in %s"), tr_strltime_rounded(tracker.nextAnnounceTime - now)); gstr << fmt::format(
_("Asking for more peers in {time_span}"),
fmt::arg("time_span", tr_strltime_rounded(tracker.nextAnnounceTime - now)));
break; break;
case TR_TRACKER_QUEUED: case TR_TRACKER_QUEUED:
@ -1916,9 +1960,12 @@ void appendAnnounceInfo(tr_tracker_view const& tracker, time_t const now, Gtk::T
case TR_TRACKER_ACTIVE: case TR_TRACKER_ACTIVE:
gstr << '\n'; gstr << '\n';
gstr << text_dir_mark[direction]; gstr << text_dir_mark[direction];
gstr << gtr_sprintf( gstr << fmt::format(
_("Asking for more peers now… <small>%s</small>"), // {markup_begin} and {markup_end} should surround the time_span
tr_strltime_rounded(now - tracker.lastAnnounceStartTime)); _("Asking for more peers now… {markup_begin}{time_span}{markup_end}"),
fmt::arg("markup_begin", "<small>"),
fmt::arg("time_span", tr_strltime_rounded(now - tracker.lastAnnounceStartTime)),
fmt::arg("markup_end", "</small>"));
break; break;
default: default:
@ -1936,22 +1983,26 @@ void appendScrapeInfo(tr_tracker_view const& tracker, time_t const now, Gtk::Tex
if (tracker.lastScrapeSucceeded) if (tracker.lastScrapeSucceeded)
{ {
gstr << gtr_sprintf( gstr << fmt::format(
_("Tracker had %s%'d seeders and %'d leechers%s %s ago"), // {markup_begin} and {markup_end} should surround the seeder/leecher text
success_markup_begin, _("Tracker had {markup_begin}{seeder_count} {seeder_or_seeders} and {leecher_count} {leecher_or_leechers}{markup_end} {time_span} ago"),
tracker.seederCount, fmt::arg("seeder_count", tracker.seederCount),
tracker.leecherCount, fmt::arg("seeder_or_seeders", ngettext("seeder", "seeders", tracker.seederCount)),
success_markup_end, fmt::arg("leecher_count", tracker.leecherCount),
timebuf); fmt::arg("leecher_or_leechers", ngettext("leecher", "leechers", tracker.leecherCount)),
fmt::arg("time_span", timebuf),
fmt::arg("markup_begin", SuccessMarkupBegin),
fmt::arg("markup_end", SuccessMarkupEnd));
} }
else else
{ {
gstr << gtr_sprintf( gstr << fmt::format(
_("Got a scrape error \"%s%s%s\" %s ago"), // {markup_begin} and {markup_end} should surround the error text
err_markup_begin, _("Got a scrape error '{markup_begin}{error}{markup_end}' {time_span} ago"),
tracker.lastScrapeResult, fmt::arg("error", Glib::Markup::escape_text(tracker.lastScrapeResult)),
err_markup_end, fmt::arg("time_span", timebuf),
timebuf); fmt::arg("markup_begin", ErrMarkupBegin),
fmt::arg("markup_end", ErrMarkupEnd));
} }
} }
@ -1963,7 +2014,9 @@ void appendScrapeInfo(tr_tracker_view const& tracker, time_t const now, Gtk::Tex
case TR_TRACKER_WAITING: case TR_TRACKER_WAITING:
gstr << '\n'; gstr << '\n';
gstr << text_dir_mark[direction]; gstr << text_dir_mark[direction];
gstr << gtr_sprintf(_("Asking for peer counts in %s"), tr_strltime_rounded(tracker.nextScrapeTime - now)); gstr << fmt::format(
_("Asking for peer counts in {time_span}"),
fmt::arg("time_span", tr_strltime_rounded(tracker.nextScrapeTime - now)));
break; break;
case TR_TRACKER_QUEUED: case TR_TRACKER_QUEUED:
@ -1975,9 +2028,11 @@ void appendScrapeInfo(tr_tracker_view const& tracker, time_t const now, Gtk::Tex
case TR_TRACKER_ACTIVE: case TR_TRACKER_ACTIVE:
gstr << '\n'; gstr << '\n';
gstr << text_dir_mark[direction]; gstr << text_dir_mark[direction];
gstr << gtr_sprintf( gstr << fmt::format(
_("Asking for peer counts now… <small>%s</small>"), _("Asking for peer counts now… {markup_begin}{time_span}{markup_end}"),
tr_strltime_rounded(now - tracker.lastScrapeStartTime)); fmt::arg("markup_begin", "<small>"),
fmt::arg("time_span", tr_strltime_rounded(now - tracker.lastScrapeStartTime)),
fmt::arg("markup_end", "</small>"));
break; break;
default: default:
@ -1995,7 +2050,7 @@ void buildTrackerSummary(
// hostname // hostname
gstr << text_dir_mark[direction]; gstr << text_dir_mark[direction];
gstr << (tracker.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 << Glib::Markup::escape_text(!key.empty() ? fmt::format("{} - {}", tracker.host, key) : tracker.host);
gstr << (tracker.isBackup ? "</i>" : "</b>"); gstr << (tracker.isBackup ? "</i>" : "</b>");
if (!tracker.isBackup) if (!tracker.isBackup)
@ -2261,7 +2316,7 @@ void DetailsDialog::Impl::on_edit_trackers()
int const torrent_id = tr_torrentId(tor); int const torrent_id = tr_torrentId(tor);
auto d = std::make_shared<Gtk::Dialog>( auto d = std::make_shared<Gtk::Dialog>(
gtr_sprintf(_("%s - Edit Trackers"), tr_torrentName(tor)), fmt::format(_("{torrent_name} - Edit Trackers"), fmt::arg("torrent_name", tr_torrentName(tor))),
dialog_, dialog_,
Gtk::DIALOG_MODAL | Gtk::DIALOG_DESTROY_WITH_PARENT); Gtk::DIALOG_MODAL | Gtk::DIALOG_DESTROY_WITH_PARENT);
d->add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL); d->add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
@ -2371,7 +2426,7 @@ void DetailsDialog::Impl::on_tracker_list_add_button_clicked()
guint row; guint row;
auto w = std::make_shared<Gtk::Dialog>( auto w = std::make_shared<Gtk::Dialog>(
gtr_sprintf(_("%s - Add Tracker"), tr_torrentName(tor)), fmt::format(_("{torrent_name} - Add Tracker"), fmt::arg("torrent_name", tr_torrentName(tor))),
dialog_, dialog_,
Gtk::DIALOG_DESTROY_WITH_PARENT); Gtk::DIALOG_DESTROY_WITH_PARENT);
w->add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL); w->add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
@ -2611,7 +2666,7 @@ void DetailsDialog::Impl::set_torrents(std::vector<int> const& ids)
{ {
int const id = ids.front(); int const id = ids.front();
auto const* tor = core_->find_torrent(id); auto const* tor = core_->find_torrent(id);
title = gtr_sprintf(_("%s Properties"), tr_torrentName(tor)); title = fmt::format(_("{torrent_name} Properties"), fmt::arg("torrent_name", tr_torrentName(tor)));
file_list_->set_torrent(id); file_list_->set_torrent(id);
file_list_->show(); file_list_->show();
@ -2619,10 +2674,13 @@ void DetailsDialog::Impl::set_torrents(std::vector<int> const& ids)
} }
else else
{ {
title = fmt::format(
ngettext("Properties - {count} Torrent", "Properties - {count} Torrents", len),
fmt::arg("count", len));
file_list_->clear(); file_list_->clear();
file_list_->hide(); file_list_->hide();
file_label_->show(); file_label_->show();
title = gtr_sprintf(_("%'d Torrent Properties"), len);
} }
dialog_.set_title(title); dialog_.set_title(title);

View File

@ -745,8 +745,8 @@ bool FileList::Impl::on_rename_done_idle(Glib::ustring const& path_string, Glib:
*static_cast<Gtk::Window*>(widget_.get_toplevel()), *static_cast<Gtk::Window*>(widget_.get_toplevel()),
fmt::format( fmt::format(
_("Couldn't rename '{old_path}' as '{path}': {error} ({error_code})"), _("Couldn't rename '{old_path}' as '{path}': {error} ({error_code})"),
fmt::arg("old_path", path_string.raw()), fmt::arg("old_path", path_string),
fmt::arg("path", newname.raw()), fmt::arg("path", newname),
fmt::arg("error", tr_strerror(error)), fmt::arg("error", tr_strerror(error)),
fmt::arg("error_code", error)), fmt::arg("error_code", error)),
false, false,

View File

@ -711,7 +711,9 @@ bool FilterBar::Impl::update_count_label()
/* set the text */ /* set the text */
show_lb_->set_markup_with_mnemonic( show_lb_->set_markup_with_mnemonic(
visibleCount == std::min(activityCount, trackerCount) ? _("_Show:") : gtr_sprintf(_("_Show %'d of:"), visibleCount)); visibleCount == std::min(activityCount, trackerCount) ?
_("_Show:") :
fmt::format(_("_Show {count:L} of:"), fmt::arg("count", visibleCount)));
show_lb_->steal_data(DIRTY_KEY); show_lb_->steal_data(DIRTY_KEY);
return false; return false;

View File

@ -8,6 +8,8 @@
#include <glibmm/i18n.h> #include <glibmm/i18n.h>
#include <fmt/core.h>
#include <libtransmission/utils.h> #include <libtransmission/utils.h>
#include "FreeSpaceLabel.h" #include "FreeSpaceLabel.h"
@ -47,9 +49,8 @@ bool FreeSpaceLabel::Impl::on_freespace_timer()
} }
auto const bytes = tr_dirSpace(dir_).free; auto const bytes = tr_dirSpace(dir_).free;
auto const text = bytes < 0 ? _("Error") : gtr_sprintf(_("%s free"), tr_strlsize(bytes)); auto const text = bytes < 0 ? _("Error") : fmt::format(_("{disk_space} free"), fmt::arg("disk_space", tr_strlsize(bytes)));
auto const markup = gtr_sprintf("<i>%s</i>", text); label_.set_markup(fmt::format("<i>{}</i>", text));
label_.set_markup(markup);
return true; return true;
} }

View File

@ -220,17 +220,15 @@ void MainWindow::Impl::syncAltSpeedButton()
bool const b = gtr_pref_flag_get(TR_KEY_alt_speed_enabled); bool const b = gtr_pref_flag_get(TR_KEY_alt_speed_enabled);
char const* const stock = b ? "alt-speed-on" : "alt-speed-off"; char const* const stock = b ? "alt-speed-on" : "alt-speed-off";
auto const u = tr_formatter_speed_KBps(gtr_pref_int_get(TR_KEY_alt_speed_up));
auto const d = tr_formatter_speed_KBps(gtr_pref_int_get(TR_KEY_alt_speed_down));
auto const str = b ? gtr_sprintf(_("Click to disable Alternative Speed Limits\n (%1$s down, %2$s up)"), d, u) :
gtr_sprintf(_("Click to enable Alternative Speed Limits\n (%1$s down, %2$s up)"), d, u);
alt_speed_button_->set_active(b); alt_speed_button_->set_active(b);
alt_speed_image_->set_from_icon_name(stock, Gtk::BuiltinIconSize::ICON_SIZE_MENU); alt_speed_image_->set_from_icon_name(stock, Gtk::BuiltinIconSize::ICON_SIZE_MENU);
alt_speed_button_->set_halign(Gtk::ALIGN_CENTER); alt_speed_button_->set_halign(Gtk::ALIGN_CENTER);
alt_speed_button_->set_valign(Gtk::ALIGN_CENTER); alt_speed_button_->set_valign(Gtk::ALIGN_CENTER);
alt_speed_button_->set_tooltip_text(str); alt_speed_button_->set_tooltip_text(fmt::format(
b ? _("Click to disable Alternative Speed Limits\n ({download_speed} down, {upload_speed} up)") :
_("Click to enable Alternative Speed Limits\n ({download_speed} down, {upload_speed} up)"),
fmt::arg("download_speed", tr_formatter_speed_KBps(gtr_pref_int_get(TR_KEY_alt_speed_down))),
fmt::arg("upload_speed", tr_formatter_speed_KBps(gtr_pref_int_get(TR_KEY_alt_speed_up)))));
} }
void MainWindow::Impl::alt_speed_toggled_cb() void MainWindow::Impl::alt_speed_toggled_cb()
@ -381,7 +379,7 @@ void MainWindow::Impl::onOptionsClicked(Gtk::Button* button)
gtr_label_set_text( gtr_label_set_text(
*static_cast<Gtk::Label*>(ratio_on_item_->get_child()), *static_cast<Gtk::Label*>(ratio_on_item_->get_child()),
gtr_sprintf(_("Stop at Ratio (%s)"), tr_strlratio(gtr_pref_double_get(TR_KEY_ratio_limit)))); fmt::format(_("Stop at Ratio ({ratio})"), fmt::arg("ratio", tr_strlratio(gtr_pref_double_get(TR_KEY_ratio_limit)))));
(gtr_pref_flag_get(TR_KEY_ratio_limit_enabled) ? ratio_on_item_ : ratio_off_item_)->set_active(true); (gtr_pref_flag_get(TR_KEY_ratio_limit_enabled) ? ratio_on_item_ : ratio_off_item_)->set_active(true);
@ -589,31 +587,28 @@ void MainWindow::Impl::updateStats()
if (auto const pch = gtr_pref_string_get(TR_KEY_statusbar_stats); pch == "session-ratio") if (auto const pch = gtr_pref_string_get(TR_KEY_statusbar_stats); pch == "session-ratio")
{ {
tr_sessionGetStats(session, &stats); tr_sessionGetStats(session, &stats);
buf = gtr_sprintf(_("Ratio: %s"), tr_strlratio(stats.ratio)); buf = fmt::format(_("Ratio: {ratio}"), tr_strlratio(stats.ratio));
} }
else if (pch == "session-transfer") else if (pch == "session-transfer")
{ {
tr_sessionGetStats(session, &stats); tr_sessionGetStats(session, &stats);
/* Translators: "size|" is here for disambiguation. Please remove it from your translation. buf = fmt::format(
%1$s is the size of the data we've downloaded C_("current session totals", "Down: {downloaded_size}, Up: {uploaded_size}"),
%2$s is the size of the data we've uploaded */ fmt::arg("downloaded_size", tr_strlsize(stats.downloadedBytes)),
buf = gtr_sprintf(Q_("Down: %1$s, Up: %2$s"), tr_strlsize(stats.downloadedBytes), tr_strlsize(stats.uploadedBytes)); fmt::arg("uploaded_size", tr_strlsize(stats.uploadedBytes)));
} }
else if (pch == "total-transfer") else if (pch == "total-transfer")
{ {
tr_sessionGetCumulativeStats(session, &stats); tr_sessionGetCumulativeStats(session, &stats);
/* Translators: "size|" is here for disambiguation. Please remove it from your translation. buf = fmt::format(
%1$s is the size of the data we've downloaded C_("all-time totals", "Down: {downloaded_size}, Up: {uploaded_size}"),
%2$s is the size of the data we've uploaded */ fmt::arg("downloaded_size", tr_strlsize(stats.downloadedBytes)),
buf = gtr_sprintf( fmt::arg("uploaded_size", tr_strlsize(stats.uploadedBytes)));
Q_("size|Down: %1$s, Up: %2$s"),
tr_strlsize(stats.downloadedBytes),
tr_strlsize(stats.uploadedBytes));
} }
else /* default is total-ratio */ else /* default is total-ratio */
{ {
tr_sessionGetCumulativeStats(session, &stats); tr_sessionGetCumulativeStats(session, &stats);
buf = gtr_sprintf(_("Ratio: %s"), tr_strlratio(stats.ratio)); buf = fmt::format(_("Ratio: {ratio}"), fmt::arg("ratio", tr_strlratio(stats.ratio)));
} }
stats_lb_->set_text(buf); stats_lb_->set_text(buf);
@ -625,25 +620,25 @@ void MainWindow::Impl::updateSpeeds()
if (session != nullptr) if (session != nullptr)
{ {
double upSpeed = 0; auto dn_count = int{};
double downSpeed = 0; auto dn_speed = double{};
int upCount = 0; auto up_count = int{};
int downCount = 0; auto up_speed = double{};
auto const model = core_->get_model();
auto const model = core_->get_model();
for (auto const& row : model->children()) for (auto const& row : model->children())
{ {
upSpeed += row.get_value(torrent_cols.speed_up); dn_count += row.get_value(torrent_cols.active_peers_down);
upCount += row.get_value(torrent_cols.active_peers_up); dn_speed += row.get_value(torrent_cols.speed_down);
downSpeed += row.get_value(torrent_cols.speed_down); up_count += row.get_value(torrent_cols.active_peers_up);
downCount += row.get_value(torrent_cols.active_peers_down); up_speed += row.get_value(torrent_cols.speed_up);
} }
dl_lb_->set_text(gtr_sprintf("%s %s", tr_formatter_speed_KBps(downSpeed), gtr_get_unicode_string(GtrUnicode::Down))); dl_lb_->set_text(fmt::format(_("{download_speed} ▼"), fmt::arg("download_speed", dn_speed)));
dl_lb_->set_visible(downCount > 0); dl_lb_->set_visible(dn_count > 0);
ul_lb_->set_text(gtr_sprintf("%s %s", tr_formatter_speed_KBps(upSpeed), gtr_get_unicode_string(GtrUnicode::Up))); ul_lb_->set_text(fmt::format(_("{upload_speed} ▲"), fmt::arg("upload_speed", up_speed)));
ul_lb_->set_visible(downCount > 0 || upCount > 0); ul_lb_->set_visible(dn_count > 0 || up_count > 0);
} }
} }

View File

@ -111,11 +111,11 @@ bool MakeProgressDialog::onProgressDialogRefresh()
/* progress label */ /* progress label */
if (!builder_.isDone) if (!builder_.isDone)
{ {
str = gtr_sprintf(_("Creating \"%s\""), base); str = fmt::format(_("Creating '{path}'"), fmt::arg("path", base));
} }
else if (builder_.result == TrMakemetaResult::OK) else if (builder_.result == TrMakemetaResult::OK)
{ {
str = gtr_sprintf(_("Created \"%s\"!"), base); str = fmt::format(_("Created '{path}'"), fmt::arg("path", base));
} }
else if (builder_.result == TrMakemetaResult::CANCELLED) else if (builder_.result == TrMakemetaResult::CANCELLED)
{ {
@ -130,7 +130,7 @@ bool MakeProgressDialog::onProgressDialogRefresh()
str = fmt::format( str = fmt::format(
_("Couldn't read '{path}': {error} ({error_code})"), _("Couldn't read '{path}': {error} ({error_code})"),
fmt::arg("path", builder_.errfile), fmt::arg("path", builder_.errfile),
fmt::arg("error", Glib::strerror(builder_.my_errno).raw()), fmt::arg("error", Glib::strerror(builder_.my_errno)),
fmt::arg("error_code", builder_.my_errno)); fmt::arg("error_code", builder_.my_errno));
} }
else if (builder_.result == TrMakemetaResult::ERR_IO_WRITE) else if (builder_.result == TrMakemetaResult::ERR_IO_WRITE)
@ -138,7 +138,7 @@ bool MakeProgressDialog::onProgressDialogRefresh()
str = fmt::format( str = fmt::format(
_("Couldn't save '{path}': {error} ({error_code})"), _("Couldn't save '{path}': {error} ({error_code})"),
fmt::arg("path", builder_.errfile), fmt::arg("path", builder_.errfile),
fmt::arg("error", Glib::strerror(builder_.my_errno).raw()), fmt::arg("error", Glib::strerror(builder_.my_errno)),
fmt::arg("error_code", builder_.my_errno)); fmt::arg("error_code", builder_.my_errno));
} }
else else
@ -158,7 +158,7 @@ bool MakeProgressDialog::onProgressDialogRefresh()
/* how much data we've scanned through to generate checksums */ /* how much data we've scanned through to generate checksums */
str = fmt::format( str = fmt::format(
_("Scanned {file_size}"), _("Scanned {file_size}"),
fmt::arg("file_size", tr_strlsize((uint64_t)builder_.pieceIndex * (uint64_t)builder_.pieceSize).raw())); fmt::arg("file_size", tr_strlsize((uint64_t)builder_.pieceIndex * (uint64_t)builder_.pieceSize)));
} }
progress_bar_->set_fraction(fraction); progress_bar_->set_fraction(fraction);
@ -276,7 +276,7 @@ void MakeDialog::Impl::onResponse(int response)
/* destination file */ /* destination file */
auto const dir = destination_chooser_->get_filename(); auto const dir = destination_chooser_->get_filename();
auto const base = Glib::path_get_basename(builder_->top); auto const base = Glib::path_get_basename(builder_->top);
auto const target = gtr_sprintf("%s/%s.torrent", dir, base).raw(); auto const target = gtr_sprintf("%s/%s.torrent", dir, base);
/* build the array of trackers */ /* build the array of trackers */
auto const tracker_text = announce_text_buffer_->get_text(false); auto const tracker_text = announce_text_buffer_->get_text(false);
@ -498,9 +498,8 @@ MakeDialog::Impl::Impl(MakeDialog& dialog, Glib::RefPtr<Session> const& core)
fr->add(*sw); fr->add(*sw);
v->pack_start(*fr, true, true, 0); v->pack_start(*fr, true, true, 0);
auto* l = Gtk::make_managed<Gtk::Label>(); auto* l = Gtk::make_managed<Gtk::Label>();
l->set_markup( l->set_markup(_(
_("To add a backup URL, add it on the line after the primary URL.\n" "To add a backup URL, add it on the next line after a primary URL.\nTo add a new primary URL, add it after a blank line."));
"To add another primary URL, add it after a blank line."));
l->set_justify(Gtk::JUSTIFY_LEFT); l->set_justify(Gtk::JUSTIFY_LEFT);
l->set_halign(Gtk::ALIGN_START); l->set_halign(Gtk::ALIGN_START);
l->set_valign(Gtk::ALIGN_CENTER); l->set_valign(Gtk::ALIGN_CENTER);

View File

@ -188,7 +188,7 @@ void MessageLogWindow::Impl::doSave(Gtk::Window& parent, Glib::ustring const& fi
parent, parent,
fmt::format( fmt::format(
_("Couldn't save '{path}': {error} ({error_code})"), _("Couldn't save '{path}': {error} ({error_code})"),
fmt::arg("path", filename.raw()), fmt::arg("path", filename),
fmt::arg("error", g_strerror(errcode)), fmt::arg("error", g_strerror(errcode)),
fmt::arg("error_code", errcode)), fmt::arg("error_code", errcode)),
false, false,

View File

@ -171,7 +171,7 @@ void OptionsDialog::Impl::updateTorrent()
} }
/** /**
* When the source .torrent file is deleted * When the source torrent file is deleted
* (such as, if it was a temp file that a web browser passed to us), * (such as, if it was a temp file that a web browser passed to us),
* gtk invokes this callback and `filename' will be nullptr. * gtk invokes this callback and `filename' will be nullptr.
* The `filename' tests here are to prevent us from losing the current * The `filename' tests here are to prevent us from losing the current
@ -292,7 +292,7 @@ OptionsDialog::Impl::Impl(
filename_ = tr_ctorGetSourceFile(ctor_.get()) != nullptr ? tr_ctorGetSourceFile(ctor_.get()) : ""; filename_ = tr_ctorGetSourceFile(ctor_.get()) != nullptr ? tr_ctorGetSourceFile(ctor_.get()) : "";
downloadDir_ = str; downloadDir_ = str;
file_list_ = Gtk::make_managed<FileList>(core_, 0); file_list_ = Gtk::make_managed<FileList>(core_, 0);
trash_check_ = Gtk::make_managed<Gtk::CheckButton>(_("Mo_ve .torrent file to the trash"), true); trash_check_ = Gtk::make_managed<Gtk::CheckButton>(_("Mo_ve torrent file to the trash"), true);
run_check_ = Gtk::make_managed<Gtk::CheckButton>(_("_Start when added"), true); run_check_ = Gtk::make_managed<Gtk::CheckButton>(_("_Start when added"), true);
priority_combo_ = gtr_priority_combo_new(); priority_combo_ = gtr_priority_combo_new();
@ -376,7 +376,7 @@ OptionsDialog::Impl::Impl(
run_check_->set_active(!flag); run_check_->set_active(!flag);
grid->attach(*run_check_, 0, row, 2, 1); grid->attach(*run_check_, 0, row, 2, 1);
/* "trash .torrent file" row */ /* "trash torrent file" row */
row++; row++;
if (!tr_ctorGetDeleteSource(ctor_.get(), &flag)) if (!tr_ctorGetDeleteSource(ctor_.get(), &flag))

View File

@ -254,7 +254,7 @@ Gtk::Widget* PrefsDialog::Impl::downloadingPage()
t->add_section_title(row, C_("Gerund", "Adding")); t->add_section_title(row, C_("Gerund", "Adding"));
{ {
auto* l = new_check_button(_("Automatically add .torrent files _from:"), TR_KEY_watch_dir_enabled, core_); auto* l = new_check_button(_("Automatically add torrent files _from:"), TR_KEY_watch_dir_enabled, core_);
auto* w = new_path_chooser_button(TR_KEY_watch_dir, core_); auto* w = new_path_chooser_button(TR_KEY_watch_dir, core_);
w->set_sensitive(gtr_pref_flag_get(TR_KEY_watch_dir_enabled)); w->set_sensitive(gtr_pref_flag_get(TR_KEY_watch_dir_enabled));
l->signal_toggled().connect([l, w]() { target_cb(l, w); }); l->signal_toggled().connect([l, w]() { target_cb(l, w); });
@ -267,7 +267,7 @@ Gtk::Widget* PrefsDialog::Impl::downloadingPage()
t->add_wide_control( t->add_wide_control(
row, row,
*new_check_button(_("Mo_ve .torrent file to the trash"), TR_KEY_trash_original_torrent_files, core_)); *new_check_button(_("Mo_ve torrent file to the trash"), TR_KEY_trash_original_torrent_files, core_));
t->add_row(row, _("Save to _Location:"), *new_path_chooser_button(TR_KEY_download_dir, core_)); t->add_row(row, _("Save to _Location:"), *new_path_chooser_button(TR_KEY_download_dir, core_));
@ -415,7 +415,7 @@ void updateBlocklistText(Gtk::Label* w, Glib::RefPtr<Session> const& core)
auto const msg = fmt::format( auto const msg = fmt::format(
ngettext("Blocklist has {count} entry", "Blocklist has {count} entries", n), ngettext("Blocklist has {count} entry", "Blocklist has {count} entries", n),
fmt::arg("count", n)); fmt::arg("count", n));
w->set_markup(gtr_sprintf("<i>%s</i>", msg.c_str())); w->set_markup(fmt::format("<i>{}</i>", msg));
} }
/* prefs dialog is being destroyed, so stop listening to blocklist updates */ /* prefs dialog is being destroyed, so stop listening to blocklist updates */
@ -887,7 +887,11 @@ Gtk::Widget* PrefsDialog::Impl::speedPage()
t->add_section_title(row, _("Speed Limits")); t->add_section_title(row, _("Speed Limits"));
{ {
auto* w = new_check_button(gtr_sprintf(_("_Upload (%s):"), _(speed_K_str)), TR_KEY_speed_limit_up_enabled, core_); auto* w = new_check_button(
// checkbox to limit upload speed
fmt::format(_("_Upload ({speed_units}):"), fmt::arg("speed_units", speed_K_str)),
TR_KEY_speed_limit_up_enabled,
core_);
auto* w2 = new_spin_button(TR_KEY_speed_limit_up, core_, 0, INT_MAX, 5); auto* w2 = new_spin_button(TR_KEY_speed_limit_up, core_, 0, INT_MAX, 5);
w2->set_sensitive(gtr_pref_flag_get(TR_KEY_speed_limit_up_enabled)); w2->set_sensitive(gtr_pref_flag_get(TR_KEY_speed_limit_up_enabled));
w->signal_toggled().connect([w, w2]() { target_cb(w, w2); }); w->signal_toggled().connect([w, w2]() { target_cb(w, w2); });
@ -895,7 +899,11 @@ Gtk::Widget* PrefsDialog::Impl::speedPage()
} }
{ {
auto* w = new_check_button(gtr_sprintf(_("_Download (%s):"), _(speed_K_str)), TR_KEY_speed_limit_down_enabled, core_); auto* w = new_check_button(
// checkbox to limit download speed
fmt::format(_("_Download ({speed_units}):"), fmt::arg("speed_units", speed_K_str)),
TR_KEY_speed_limit_down_enabled,
core_);
auto* w2 = new_spin_button(TR_KEY_speed_limit_down, core_, 0, INT_MAX, 5); auto* w2 = new_spin_button(TR_KEY_speed_limit_down, core_, 0, INT_MAX, 5);
w2->set_sensitive(gtr_pref_flag_get(TR_KEY_speed_limit_down_enabled)); w2->set_sensitive(gtr_pref_flag_get(TR_KEY_speed_limit_down_enabled));
w->signal_toggled().connect([w, w2]() { target_cb(w, w2); }); w->signal_toggled().connect([w, w2]() { target_cb(w, w2); });
@ -906,7 +914,7 @@ Gtk::Widget* PrefsDialog::Impl::speedPage()
{ {
auto* h = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL, GUI_PAD); auto* h = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL, GUI_PAD);
auto* w = Gtk::make_managed<Gtk::Label>(gtr_sprintf("<b>%s</b>", _("Alternative Speed Limits"))); auto* w = Gtk::make_managed<Gtk::Label>(fmt::format("<b>{}</b>", _("Alternative Speed Limits")));
w->set_halign(Gtk::ALIGN_START); w->set_halign(Gtk::ALIGN_START);
w->set_valign(Gtk::ALIGN_CENTER); w->set_valign(Gtk::ALIGN_CENTER);
w->set_use_markup(true); w->set_use_markup(true);
@ -917,7 +925,7 @@ Gtk::Widget* PrefsDialog::Impl::speedPage()
{ {
auto* w = Gtk::make_managed<Gtk::Label>( auto* w = Gtk::make_managed<Gtk::Label>(
gtr_sprintf("<small>%s</small>", _("Override normal speed limits manually or at scheduled times"))); fmt::format("<small>{}</small>", _("Override normal speed limits manually or at scheduled times")));
w->set_use_markup(true); w->set_use_markup(true);
w->set_halign(Gtk::ALIGN_START); w->set_halign(Gtk::ALIGN_START);
w->set_valign(Gtk::ALIGN_CENTER); w->set_valign(Gtk::ALIGN_CENTER);
@ -926,12 +934,14 @@ Gtk::Widget* PrefsDialog::Impl::speedPage()
t->add_row( t->add_row(
row, row,
gtr_sprintf(_("U_pload (%s):"), _(speed_K_str)), // labels a spinbutton for alternate upload speed limits
fmt::format(_("U_pload ({speed_units}):"), fmt::arg("speed_units", speed_K_str)),
*new_spin_button(TR_KEY_alt_speed_up, core_, 0, INT_MAX, 5)); *new_spin_button(TR_KEY_alt_speed_up, core_, 0, INT_MAX, 5));
t->add_row( t->add_row(
row, row,
gtr_sprintf(_("Do_wnload (%s):"), _(speed_K_str)), // labels a spinbutton for alternate download speed limits
fmt::format(_("Do_wnload ({speed_units}):"), fmt::arg("speed_units", speed_K_str)),
*new_spin_button(TR_KEY_alt_speed_down, core_, 0, INT_MAX, 5)); *new_spin_button(TR_KEY_alt_speed_down, core_, 0, INT_MAX, 5));
{ {
@ -939,6 +949,7 @@ Gtk::Widget* PrefsDialog::Impl::speedPage()
auto* start_combo = new_time_combo(core_, TR_KEY_alt_speed_time_begin); auto* start_combo = new_time_combo(core_, TR_KEY_alt_speed_time_begin);
page->sched_widgets.push_back(start_combo); page->sched_widgets.push_back(start_combo);
h->pack_start(*start_combo, true, true, 0); h->pack_start(*start_combo, true, true, 0);
// label goes between two time selectors, e.g. "limit speeds from [time] to [time]"
auto* to_label = Gtk::make_managed<Gtk::Label>(_(" _to "), true); auto* to_label = Gtk::make_managed<Gtk::Label>(_(" _to "), true);
page->sched_widgets.push_back(to_label); page->sched_widgets.push_back(to_label);
h->pack_start(*to_label, false, false, 0); h->pack_start(*to_label, false, false, 0);
@ -1006,7 +1017,10 @@ network_page_data::~network_page_data()
void onPortTested(bool isOpen, network_page_data* data) void onPortTested(bool isOpen, network_page_data* data)
{ {
data->portLabel->set_markup(isOpen ? _("Port is <b>open</b>") : _("Port is <b>closed</b>")); data->portLabel->set_markup(fmt::format(
isOpen ? _("Port is {markup_begin}open{markup_end}") : _("Port is {markup_begin}closed{markup_end}"),
fmt::arg("markup_begin", "<b>"),
fmt::arg("markup_end", "</b>")));
data->portButton->set_sensitive(true); data->portButton->set_sensitive(true);
data->portSpin->set_sensitive(true); data->portSpin->set_sensitive(true);
} }

View File

@ -10,6 +10,8 @@
#include <glibmm.h> #include <glibmm.h>
#include <glibmm/i18n.h> #include <glibmm/i18n.h>
#include <fmt/core.h>
#include <libtransmission/transmission.h> #include <libtransmission/transmission.h>
#include "HigWorkarea.h" #include "HigWorkarea.h"
@ -72,7 +74,9 @@ void RelocateDialog::Impl::startMovingNextTorrent()
torrent_ids_.pop_back(); torrent_ids_.pop_back();
message_dialog_->set_message(gtr_sprintf(_("Moving \"%s\""), tr_torrentName(tor)), true); message_dialog_->set_message(
fmt::format(_("Moving '{torrent_name}'"), fmt::arg("torrent_name", tr_torrentName(tor))),
true);
} }
/* every once in awhile, check to see if the move is done. /* every once in awhile, check to see if the move is done.

View File

@ -3,9 +3,10 @@
// A copy of this license can be found in licenses/ . // A copy of this license can be found in licenses/ .
#include <algorithm> #include <algorithm>
#include <cmath> /* pow() */ #include <cmath> // pow()
#include <cstring> // strstr #include <cstring> // strstr
#include <functional> #include <functional>
#include <iostream>
#include <map> #include <map>
#include <memory> #include <memory>
#include <string> #include <string>
@ -617,9 +618,9 @@ void rename_torrent(Glib::RefPtr<Gio::File> const& file)
{ {
auto const errmsg = fmt::format( auto const errmsg = fmt::format(
_("Couldn't rename '{old_path}' as '{path}': {error} ({error_code})"), _("Couldn't rename '{old_path}' as '{path}': {error} ({error_code})"),
fmt::arg("old_path", old_name.raw()), fmt::arg("old_path", old_name),
fmt::arg("path", new_name.raw()), fmt::arg("path", new_name),
fmt::arg("error", e.what().raw()), fmt::arg("error", e.what()),
fmt::arg("error_code", e.code())); fmt::arg("error_code", e.code()));
g_message("%s", errmsg.c_str()); g_message("%s", errmsg.c_str());
} }
@ -1037,7 +1038,7 @@ int Session::Impl::add_ctor(tr_ctor* ctor, bool do_prompt, bool do_notify)
if (tr_torrentFindFromMetainfo(get_session(), metainfo) != nullptr) if (tr_torrentFindFromMetainfo(get_session(), metainfo) != nullptr)
{ {
/* don't complain about .torrent files in the watch directory /* don't complain about torrent files in the watch directory
* that have already been added... that gets annoying and we * that have already been added... that gets annoying and we
* don't want to be nagging users to clean up their watch dirs */ * don't want to be nagging users to clean up their watch dirs */
if (tr_ctorGetSourceFile(ctor) == nullptr || !adding_from_watch_dir_) if (tr_ctorGetSourceFile(ctor) == nullptr || !adding_from_watch_dir_)
@ -1115,7 +1116,7 @@ void Session::Impl::add_file_async_callback(
{ {
if (!file->load_contents_finish(result, contents, length)) if (!file->load_contents_finish(result, contents, length))
{ {
auto const errmsg = fmt::format(_("Couldn't read '{path}'"), fmt::arg("path", file->get_parse_name().raw())); auto const errmsg = fmt::format(_("Couldn't read '{path}'"), fmt::arg("path", file->get_parse_name()));
g_message("%s", errmsg.c_str()); g_message("%s", errmsg.c_str());
} }
else if (tr_ctorSetMetainfo(ctor, contents, length, nullptr)) else if (tr_ctorSetMetainfo(ctor, contents, length, nullptr))
@ -1131,8 +1132,8 @@ void Session::Impl::add_file_async_callback(
{ {
auto const errmsg = fmt::format( auto const errmsg = fmt::format(
_("Couldn't read '{path}': {error} ({error_code})"), _("Couldn't read '{path}': {error} ({error_code})"),
fmt::arg("path", file->get_parse_name().raw()), fmt::arg("path", file->get_parse_name()),
fmt::arg("error", e.what().raw()), fmt::arg("error", e.what()),
fmt::arg("error_code", e.code())); fmt::arg("error_code", e.code()));
g_message("%s", errmsg.c_str()); g_message("%s", errmsg.c_str());
} }
@ -1182,7 +1183,8 @@ bool Session::Impl::add_file(Glib::RefPtr<Gio::File> const& file, bool do_start,
else else
{ {
tr_ctorFree(ctor); tr_ctorFree(ctor);
g_message(_("Skipping unknown torrent \"%s\""), file->get_parse_name().c_str()); std::cerr << fmt::format(_("Couldn't add torrent file '{path}'"), fmt::arg("path", file->get_parse_name()))
<< std::endl;
} }
return handled; return handled;
@ -1457,7 +1459,7 @@ bool gtr_inhibit_hibernation(guint32& cookie)
} }
catch (Glib::Error const& e) catch (Glib::Error const& e)
{ {
tr_logAddError(fmt::format(_("Couldn't inhibit desktop hibernation: {error}"), fmt::arg("error", e.what().raw()))); tr_logAddError(fmt::format(_("Couldn't inhibit desktop hibernation: {error}"), fmt::arg("error", e.what())));
} }
return success; return success;
@ -1482,7 +1484,7 @@ void gtr_uninhibit_hibernation(guint inhibit_cookie)
} }
catch (Glib::Error const& e) catch (Glib::Error const& e)
{ {
tr_logAddError(fmt::format(_("Couldn't inhibit desktop hibernation: {error}"), fmt::arg("error", e.what().raw()))); tr_logAddError(fmt::format(_("Couldn't inhibit desktop hibernation: {error}"), fmt::arg("error", e.what())));
} }
} }

View File

@ -8,6 +8,8 @@
#include <glibmm.h> #include <glibmm.h>
#include <glibmm/i18n.h> #include <glibmm/i18n.h>
#include <fmt/core.h>
#include "HigWorkarea.h" #include "HigWorkarea.h"
#include "PrefsDialog.h" #include "PrefsDialog.h"
#include "Session.h" #include "Session.h"
@ -60,6 +62,11 @@ void setLabelFromRatio(Gtk::Label* l, double d)
setLabel(l, tr_strlratio(d)); setLabel(l, tr_strlratio(d));
} }
auto startedTimesText(uint64_t n)
{
return fmt::format(ngettext("Started {count:L} time", "Started {count:L} times", n), fmt::arg("count", n));
}
} // namespace } // namespace
bool StatsDialog::Impl::updateStats() bool StatsDialog::Impl::updateStats()
@ -75,10 +82,7 @@ bool StatsDialog::Impl::updateStats()
setLabel(one_time_lb_, tr_strltime(one.secondsActive)); setLabel(one_time_lb_, tr_strltime(one.secondsActive));
setLabelFromRatio(one_ratio_lb_, one.ratio); setLabelFromRatio(one_ratio_lb_, one.ratio);
setLabel( setLabel(all_sessions_lb_, startedTimesText(all.sessionCount));
all_sessions_lb_,
gtr_sprintf(ngettext("Started %'d time", "Started %'d times", (int)all.sessionCount), (int)all.sessionCount));
setLabel(all_up_lb_, tr_strlsize(all.uploadedBytes)); setLabel(all_up_lb_, tr_strlsize(all.uploadedBytes));
setLabel(all_down_lb_, tr_strlsize(all.downloadedBytes)); setLabel(all_down_lb_, tr_strlsize(all.downloadedBytes));
setLabel(all_time_lb_, tr_strltime(all.secondsActive)); setLabel(all_time_lb_, tr_strltime(all.secondsActive));
@ -158,7 +162,7 @@ StatsDialog::Impl::Impl(StatsDialog& dialog, Glib::RefPtr<Session> const& core)
t->add_section_divider(row); t->add_section_divider(row);
t->add_section_title(row, _("Total")); t->add_section_title(row, _("Total"));
all_sessions_lb_ = Gtk::make_managed<Gtk::Label>(_("Started %'d time")); all_sessions_lb_ = Gtk::make_managed<Gtk::Label>(startedTimesText(1));
all_sessions_lb_->set_single_line_mode(true); all_sessions_lb_->set_single_line_mode(true);
t->add_label_w(row, *all_sessions_lb_); t->add_label_w(row, *all_sessions_lb_);
++row; ++row;

View File

@ -6,10 +6,13 @@
#include <climits> /* INT_MAX */ #include <climits> /* INT_MAX */
#include <cstring> // strchr() #include <cstring> // strchr()
#include <memory> #include <memory>
#include <optional>
#include <glibmm.h> #include <glibmm.h>
#include <glibmm/i18n.h> #include <glibmm/i18n.h>
#include <fmt/core.h>
#include <libtransmission/transmission.h> #include <libtransmission/transmission.h>
#include <libtransmission/utils.h> /* tr_truncd() */ #include <libtransmission/utils.h> /* tr_truncd() */
@ -33,7 +36,7 @@ auto const SmallScale = 0.9;
auto const CompactIconSize = Gtk::ICON_SIZE_MENU; auto const CompactIconSize = Gtk::ICON_SIZE_MENU;
auto const FullIconSize = Gtk::ICON_SIZE_DND; auto const FullIconSize = Gtk::ICON_SIZE_DND;
Glib::ustring getProgressString(tr_torrent const* tor, uint64_t total_size, tr_stat const* st) auto getProgressString(tr_torrent const* tor, uint64_t total_size, tr_stat const* st)
{ {
Glib::ustring gstr; Glib::ustring gstr;
@ -43,81 +46,56 @@ Glib::ustring getProgressString(tr_torrent const* tor, uint64_t total_size, tr_s
double seedRatio; double seedRatio;
bool const hasSeedRatio = tr_torrentGetSeedRatio(tor, &seedRatio); bool const hasSeedRatio = tr_torrentGetSeedRatio(tor, &seedRatio);
if (!isDone) /* downloading */ if (!isDone) // downloading
{ {
gstr += gtr_sprintf( // 50 MB of 200 MB (25%)
/* %1$s is how much we've got, gstr += fmt::format(
%2$s is how much we'll have when done, _("{current_size} of {complete_size} ({percent_done}%)"),
%3$s%% is a percentage of the two */ fmt::arg("current_size", tr_strlsize(haveTotal)),
_("%1$s of %2$s (%3$s%%)"), fmt::arg("complete_size", tr_strlsize(st->sizeWhenDone)),
tr_strlsize(haveTotal), fmt::arg("percent_done", tr_strlpercent(st->percentDone * 100.0)));
tr_strlsize(st->sizeWhenDone),
tr_strlpercent(st->percentDone * 100.0));
} }
else if (!isSeed) /* partial seeds */ else if (!isSeed && hasSeedRatio) // partial seed, seed ratio
{ {
if (hasSeedRatio) // 50 MB of 200 MB (25%), uploaded 30 MB (Ratio: X%, Goal: Y%)
{ gstr += fmt::format(
gstr += gtr_sprintf( _("{current_size} of {complete_size} ({percent_complete}%), uploaded {uploaded_size} (Ratio: {ratio}, Goal: {seed_ratio})"),
/* %1$s is how much we've got, fmt::arg("current_size", tr_strlsize(haveTotal)),
%2$s is the torrent's total size, fmt::arg("complete_size", tr_strlsize(total_size)),
%3$s%% is a percentage of the two, fmt::arg("percent_done", tr_strlpercent(st->percentComplete * 100.0)),
%4$s is how much we've uploaded, fmt::arg("uploaded_size", tr_strlsize(st->uploadedEver)),
%5$s is our upload-to-download ratio, fmt::arg("ratio", tr_strlratio(st->ratio)),
%6$s is the ratio we want to reach before we stop uploading */ fmt::arg("seed_ratio", tr_strlratio(seedRatio)));
_("%1$s of %2$s (%3$s%%), uploaded %4$s (Ratio: %5$s Goal: %6$s)"),
tr_strlsize(haveTotal),
tr_strlsize(total_size),
tr_strlpercent(st->percentComplete * 100.0),
tr_strlsize(st->uploadedEver),
tr_strlratio(st->ratio),
tr_strlratio(seedRatio));
}
else
{
gstr += gtr_sprintf(
/* %1$s is how much we've got,
%2$s is the torrent's total size,
%3$s%% is a percentage of the two,
%4$s is how much we've uploaded,
%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(total_size),
tr_strlpercent(st->percentComplete * 100.0),
tr_strlsize(st->uploadedEver),
tr_strlratio(st->ratio));
}
} }
else /* seeding */ else if (!isSeed) // partial seed, no seed ratio
{ {
if (hasSeedRatio) gstr += fmt::format(
{ _("{current_size} of {complete_size} ({percent_complete}%), uploaded {uploaded_size} (Ratio: {ratio})"),
gstr += gtr_sprintf( fmt::arg("current_size", tr_strlsize(haveTotal)),
/* %1$s is the torrent's total size, fmt::arg("complete_size", tr_strlsize(total_size)),
%2$s is how much we've uploaded, fmt::arg("percent_complete", tr_strlpercent(st->percentComplete * 100.0)),
%3$s is our upload-to-download ratio, fmt::arg("uploaded_size", tr_strlsize(st->uploadedEver)),
%4$s is the ratio we want to reach before we stop uploading */ fmt::arg("ratio", tr_strlratio(st->ratio)));
_("%1$s, uploaded %2$s (Ratio: %3$s Goal: %4$s)"), }
tr_strlsize(total_size), else if (hasSeedRatio) // seed, seed ratio
tr_strlsize(st->uploadedEver), {
tr_strlratio(st->ratio), gstr += fmt::format(
tr_strlratio(seedRatio)); _("{complete_size}, uploaded {uploaded_size} (Ratio: {ratio}, Goal: {seed_ratio})"),
} fmt::arg("complete_size", tr_strlsize(total_size)),
else /* seeding w/o a ratio */ fmt::arg("uploaded_size", tr_strlsize(st->uploadedEver)),
{ fmt::arg("ratio", tr_strlratio(st->ratio)),
gstr += gtr_sprintf( fmt::arg("seed_ratio", tr_strlratio(seedRatio)));
/* %1$s is the torrent's total size, }
%2$s is how much we've uploaded, else // seed, no seed ratio
%3$s is our upload-to-download ratio */ {
_("%1$s, uploaded %2$s (Ratio: %3$s)"), gstr += fmt::format(
tr_strlsize(total_size), _("{complete_size}, uploaded {uploaded_size} (Ratio: {ratio})"),
tr_strlsize(st->uploadedEver), fmt::arg("complete_size", tr_strlsize(total_size)),
tr_strlratio(st->ratio)); fmt::arg("uploaded_size", tr_strlsize(st->uploadedEver)),
} fmt::arg("ratio", tr_strlratio(st->ratio)));
} }
/* add time when downloading */ // add time remaining when applicable
if (st->activity == TR_STATUS_DOWNLOAD || (hasSeedRatio && st->activity == TR_STATUS_SEED)) if (st->activity == TR_STATUS_DOWNLOAD || (hasSeedRatio && st->activity == TR_STATUS_SEED))
{ {
int const eta = st->eta; int const eta = st->eta;
@ -129,37 +107,32 @@ Glib::ustring getProgressString(tr_torrent const* tor, uint64_t total_size, tr_s
} }
else else
{ {
/* time remaining */ gstr += fmt::format(_("{time_span} remaining"), fmt::arg("time_span", tr_strltime(eta)));
gstr += gtr_sprintf(_("%s remaining"), tr_strltime(eta));
} }
} }
return gstr; return gstr;
} }
Glib::ustring getShortTransferString( std::string getShortTransferString(
tr_torrent const* tor, tr_torrent const* const tor,
tr_stat const* st, tr_stat const* const st,
double uploadSpeed_KBps, double uploadSpeed_KBps,
double downloadSpeed_KBps) double downloadSpeed_KBps)
{ {
bool const haveMeta = tr_torrentHasMetadata(tor); bool const have_meta = tr_torrentHasMetadata(tor);
if (bool const haveDown = haveMeta && (st->peersSendingToUs > 0 || st->webseedsSendingToUs > 0); haveDown) if (bool const have_down = have_meta && (st->peersSendingToUs > 0 || st->webseedsSendingToUs > 0); have_down)
{ {
/* down speed, down symbol, up speed, up symbol */ return fmt::format(
return gtr_sprintf( _("{upload_speed} ▲ {download_speed} ▼"),
_("%1$s %2$s %3$s %4$s"),
tr_formatter_speed_KBps(downloadSpeed_KBps), tr_formatter_speed_KBps(downloadSpeed_KBps),
gtr_get_unicode_string(GtrUnicode::Down), tr_formatter_speed_KBps(uploadSpeed_KBps));
tr_formatter_speed_KBps(uploadSpeed_KBps),
gtr_get_unicode_string(GtrUnicode::Up));
} }
if (bool const haveUp = haveMeta && st->peersGettingFromUs > 0; haveUp) if (bool const have_up = have_meta && st->peersGettingFromUs > 0; have_up)
{ {
/* up speed, up symbol */ return fmt::format(_("{upload_speed} ▲"), tr_formatter_speed_KBps(downloadSpeed_KBps));
return gtr_sprintf(_("%1$s %2$s"), tr_formatter_speed_KBps(uploadSpeed_KBps), gtr_get_unicode_string(GtrUnicode::Up));
} }
if (st->isStalled) if (st->isStalled)
@ -170,151 +143,161 @@ Glib::ustring getShortTransferString(
return {}; return {};
} }
Glib::ustring getShortStatusString(tr_torrent const* tor, tr_stat const* st, double uploadSpeed_KBps, double downloadSpeed_KBps) std::string getShortStatusString(
tr_torrent const* const tor,
tr_stat const* const st,
double uploadSpeed_KBps,
double downloadSpeed_KBps)
{ {
Glib::ustring gstr;
switch (st->activity) switch (st->activity)
{ {
case TR_STATUS_STOPPED: case TR_STATUS_STOPPED:
gstr += st->finished ? _("Finished") : _("Paused"); return st->finished ? _("Finished") : _("Paused");
break;
case TR_STATUS_CHECK_WAIT: case TR_STATUS_CHECK_WAIT:
gstr += _("Queued for verification"); return _("Queued for verification");
break;
case TR_STATUS_DOWNLOAD_WAIT: case TR_STATUS_DOWNLOAD_WAIT:
gstr += _("Queued for download"); return _("Queued for download");
break;
case TR_STATUS_SEED_WAIT: case TR_STATUS_SEED_WAIT:
gstr += _("Queued for seeding"); return _("Queued for seeding");
break;
case TR_STATUS_CHECK: case TR_STATUS_CHECK:
gstr += gtr_sprintf(_("Verifying local data (%.1f%% tested)"), tr_truncd(st->recheckProgress * 100.0, 1)); return fmt::format(
break; _("Verifying local data ({percent_done:.1}% tested)"),
fmt::arg("percent_done", tr_truncd(st->recheckProgress * 100.0, 1)));
case TR_STATUS_DOWNLOAD: case TR_STATUS_DOWNLOAD:
case TR_STATUS_SEED: case TR_STATUS_SEED:
{ return fmt::format(
/* download/upload speed, ratio */ "{} {}",
gstr += gtr_sprintf("%s ", getShortTransferString(tor, st, uploadSpeed_KBps, downloadSpeed_KBps)); getShortTransferString(tor, st, uploadSpeed_KBps, downloadSpeed_KBps),
gstr += gtr_sprintf(_("Ratio: %s"), tr_strlratio(st->ratio)); fmt::format(_("Ratio: {ratio}"), fmt::arg("ratio", tr_strlratio(st->ratio))));
break;
}
default: default:
break; return {};
} }
return gstr;
} }
Glib::ustring getStatusString( static std::optional<std::string> getErrorString(tr_stat const* st)
{
switch (st->error)
{
case TR_STAT_TRACKER_WARNING:
return fmt::format(_("Tracker warning: '{warning}'"), fmt::arg("warning", st->errorString));
break;
case TR_STAT_TRACKER_ERROR:
return fmt::format(_("Tracker Error: '{error}'"), fmt::arg("error", st->errorString));
break;
case TR_STAT_LOCAL_ERROR:
return fmt::format(_("Local error: '{error}'"), fmt::arg("error", st->errorString));
default:
return std::nullopt;
}
}
static auto getActivityString(
tr_torrent const* const tor,
tr_stat const* const st,
double const uploadSpeed_KBps,
double const downloadSpeed_KBps)
{
switch (st->activity)
{
case TR_STATUS_STOPPED:
case TR_STATUS_CHECK_WAIT:
case TR_STATUS_CHECK:
case TR_STATUS_DOWNLOAD_WAIT:
case TR_STATUS_SEED_WAIT:
return getShortStatusString(tor, st, uploadSpeed_KBps, downloadSpeed_KBps);
case TR_STATUS_DOWNLOAD:
if (!tr_torrentHasMetadata(tor))
{
return fmt::format(
ngettext(
"Downloading metadata from {active_count} connected peer ({percent_done:d}% done)",
"Downloading metadata from {active_count} connected peers ({percent_done:d}% done)",
st->peersConnected),
fmt::arg("active_count", st->peersConnected),
fmt::arg("percent_done", 100.0 * st->metadataPercentComplete));
}
if (st->peersSendingToUs != 0 && st->webseedsSendingToUs != 0)
{
return fmt::format(
ngettext(
"Downloading from {active_count} of {connected_count} connected peer and webseed",
"Downloading from {active_count} of {connected_count} connected peers and webseeds",
st->peersConnected + st->webseedsSendingToUs),
fmt::arg("active_count", st->peersSendingToUs + st->webseedsSendingToUs),
fmt::arg("connected_count", st->peersConnected + st->webseedsSendingToUs));
}
if (st->webseedsSendingToUs != 0)
{
return fmt::format(
ngettext(
"Downloading from {active_count} webseed",
"Downloading from {active_count} webseeds",
st->webseedsSendingToUs),
fmt::arg("active_count", st->webseedsSendingToUs));
}
return fmt::format(
ngettext(
"Downloading from {active_count} of {connected_count} connected peer",
"Downloading from {active_count} of {connected_count} connected peers",
st->peersConnected),
fmt::arg("active_count", st->peersSendingToUs),
fmt::arg("connected_count", st->peersConnected));
case TR_STATUS_SEED:
return fmt::format(
ngettext(
"Seeding to {active_count} of {connected_count} connected peer",
"Seeding to {active_count} of {connected_count} connected peers",
st->peersConnected),
fmt::arg("active_count", st->peersGettingFromUs),
fmt::arg("connected_count", st->peersConnected));
default:
g_assert_not_reached();
return std::string{};
}
}
std::string getStatusString(
tr_torrent const* tor, tr_torrent const* tor,
tr_stat const* st, tr_stat const* st,
double const uploadSpeed_KBps, double const uploadSpeed_KBps,
double const downloadSpeed_KBps) double const downloadSpeed_KBps)
{ {
Glib::ustring gstr; auto status_str = std::string{};
if (st->error != 0) if (auto error_string = getErrorString(st); error_string)
{ {
char const* fmt[] = { status_str = *error_string;
nullptr,
N_("Tracker gave a warning: \"%s\""),
N_("Tracker gave an error: \"%s\""),
N_("Error: %s"),
};
gstr += gtr_sprintf(_(fmt[st->error]), st->errorString);
} }
else else
{ {
switch (st->activity) status_str = getActivityString(tor, st, uploadSpeed_KBps, downloadSpeed_KBps);
{
case TR_STATUS_STOPPED:
case TR_STATUS_CHECK_WAIT:
case TR_STATUS_CHECK:
case TR_STATUS_DOWNLOAD_WAIT:
case TR_STATUS_SEED_WAIT:
{
gstr += getShortStatusString(tor, st, uploadSpeed_KBps, downloadSpeed_KBps);
break;
}
case TR_STATUS_DOWNLOAD:
{
if (!tr_torrentHasMetadata(tor))
{
/* Downloading metadata from 2 peer (s)(50% done) */
gstr += gtr_sprintf(
_("Downloading metadata from %1$'d %2$s (%3$d%% done)"),
st->peersConnected,
ngettext("peer", "peers", st->peersConnected),
(int)(100.0 * st->metadataPercentComplete));
}
else if (st->peersSendingToUs != 0 && st->webseedsSendingToUs != 0)
{
/* Downloading from 2 of 3 peer (s) and 2 webseed (s) */
gstr += gtr_sprintf(
_("Downloading from %1$'d of %2$'d %3$s and %4$'d %5$s"),
st->peersSendingToUs,
st->peersConnected,
ngettext("peer", "peers", st->peersConnected),
st->webseedsSendingToUs,
ngettext("web seed", "web seeds", st->webseedsSendingToUs));
}
else if (st->webseedsSendingToUs != 0)
{
/* Downloading from 3 web seed (s) */
gstr += gtr_sprintf(
_("Downloading from %1$'d %2$s"),
st->webseedsSendingToUs,
ngettext("web seed", "web seeds", st->webseedsSendingToUs));
}
else
{
/* Downloading from 2 of 3 peer (s) */
gstr += gtr_sprintf(
_("Downloading from %1$'d of %2$'d %3$s"),
st->peersSendingToUs,
st->peersConnected,
ngettext("peer", "peers", st->peersConnected));
}
break;
}
case TR_STATUS_SEED:
gstr += gtr_sprintf(
ngettext(
"Seeding to %1$'d of %2$'d connected peer",
"Seeding to %1$'d of %2$'d connected peers",
st->peersConnected),
st->peersGettingFromUs,
st->peersConnected);
break;
default:
g_assert_not_reached();
}
} }
if (st->activity != TR_STATUS_CHECK_WAIT && st->activity != TR_STATUS_CHECK && st->activity != TR_STATUS_DOWNLOAD_WAIT && if (st->activity != TR_STATUS_CHECK_WAIT && st->activity != TR_STATUS_CHECK && st->activity != TR_STATUS_DOWNLOAD_WAIT &&
st->activity != TR_STATUS_SEED_WAIT && st->activity != TR_STATUS_STOPPED) st->activity != TR_STATUS_SEED_WAIT && st->activity != TR_STATUS_STOPPED)
{ {
auto const buf = getShortTransferString(tor, st, uploadSpeed_KBps, downloadSpeed_KBps); if (auto const buf = getShortTransferString(tor, st, uploadSpeed_KBps, downloadSpeed_KBps); !std::empty(buf))
if (!buf.empty())
{ {
gstr += gtr_sprintf(" - %s", buf); status_str += fmt::format(" - {}", buf);
} }
} }
return gstr; return status_str;
} }
} // namespace } // namespace

View File

@ -100,31 +100,29 @@ Glib::ustring tr_strltime(time_t seconds)
} }
auto const days = (int)(seconds / 86400); auto const days = (int)(seconds / 86400);
auto const d = fmt::format(ngettext("{days} day", "{days} days", days), fmt::arg("days", days));
int const hours = (seconds % 86400) / 3600; int const hours = (seconds % 86400) / 3600;
int const minutes = (seconds % 3600) / 60; auto const h = fmt::format(ngettext("{hours} hour", "{hours} hours", hours), fmt::arg("hours", hours));
seconds = (seconds % 3600) % 60;
auto const d = gtr_sprintf(ngettext("%'d day", "%'d days", days), days);
auto const h = gtr_sprintf(ngettext("%'d hour", "%'d hours", hours), hours);
auto const m = gtr_sprintf(ngettext("%'d minute", "%'d minutes", minutes), minutes);
auto const s = gtr_sprintf(ngettext("%'d second", "%'d seconds", (int)seconds), (int)seconds);
if (days != 0) if (days != 0)
{ {
return (days >= 4 || hours == 0) ? d : gtr_sprintf("%s, %s", d, h); return (days >= 4 || hours == 0) ? d : fmt::format("{}, {}", d, h);
} }
else if (hours != 0)
int const minutes = (seconds % 3600) / 60;
auto const m = fmt::format(ngettext("{minutes} minute", "{minutes} minutes", minutes), fmt::arg("minutes", minutes));
if (hours != 0)
{ {
return (hours >= 4 || minutes == 0) ? h : gtr_sprintf("%s, %s", h, m); return (hours >= 4 || minutes == 0) ? h : fmt::format("{}, {}", h, m);
} }
else if (minutes != 0)
seconds = (seconds % 3600) % 60;
auto const s = fmt::format(ngettext("{seconds} second", "{seconds} seconds", seconds), fmt::arg("seconds", seconds));
if (minutes != 0)
{ {
return (minutes >= 4 || seconds == 0) ? m : gtr_sprintf("%s, %s", m, s); return (minutes >= 4 || seconds == 0) ? m : fmt::format("{}, {}", m, s);
}
else
{
return s;
} }
return s;
} }
namespace namespace
@ -154,14 +152,14 @@ void gtr_add_torrent_error_dialog(Gtk::Widget& child, tr_torrent* duplicate_torr
if (duplicate_torrent != nullptr) if (duplicate_torrent != nullptr)
{ {
secondary = gtr_sprintf( secondary = fmt::format(
_("The torrent file \"%s\" is already in use by \"%s.\""), _("The torrent file '{path}' is already in use by '{torrent_name}'."),
filename, fmt::arg("path", filename),
tr_torrentName(duplicate_torrent)); fmt::arg("torrent_name", tr_torrentName(duplicate_torrent)));
} }
else else
{ {
secondary = gtr_sprintf(_("Unable to add torrent file \"%s\"."), filename); secondary = fmt::format(_("Couldn't add torrent file '{path}'"), fmt::arg("path", filename));
} }
auto w = std::make_shared<Gtk::MessageDialog>( auto w = std::make_shared<Gtk::MessageDialog>(
@ -452,13 +450,13 @@ void gtr_unrecognized_url_dialog(Gtk::Widget& parent, Glib::ustring const& url)
auto w = std::make_shared<Gtk::MessageDialog>( auto w = std::make_shared<Gtk::MessageDialog>(
*window, *window,
fmt::format(_("Unsupported URL: '{url}'"), fmt::arg("url", url.raw())), fmt::format(_("Unsupported URL: '{url}'"), fmt::arg("url", url)),
false /*use markup*/, false /*use markup*/,
Gtk::MESSAGE_ERROR, Gtk::MESSAGE_ERROR,
Gtk::BUTTONS_CLOSE, Gtk::BUTTONS_CLOSE,
true /*modal*/); true /*modal*/);
gstr += gtr_sprintf(_("Transmission doesn't know how to use \"%s\""), url); gstr += fmt::format(_("Transmission doesn't know how to use '{url}'"), fmt::arg("url", url));
if (tr_magnet_metainfo{}.parseMagnet(url.raw())) if (tr_magnet_metainfo{}.parseMagnet(url.raw()))
{ {

View File

@ -14,6 +14,9 @@
#include <glibmm.h> #include <glibmm.h>
#include <gtkmm.h> #include <gtkmm.h>
#include <fmt/core.h>
#include <fmt/format.h>
#include <libtransmission/transmission.h> #include <libtransmission/transmission.h>
#include <libtransmission/tr-macros.h> #include <libtransmission/tr-macros.h>
@ -204,6 +207,16 @@ struct std::hash<Glib::ustring>
} }
}; };
template<>
struct fmt::formatter<Glib::ustring> : formatter<std::string>
{
template<typename FormatContext>
auto format(Glib::ustring const& ustr, FormatContext& ctx) const
{
return formatter<std::string>::format(ustr.raw(), ctx);
}
};
namespace Glib namespace Glib
{ {

View File

@ -225,7 +225,7 @@ bool tr_announce_list::canAdd(tr_url_parsed_t const& announce)
bool tr_announce_list::save(std::string const& torrent_file, tr_error** error) const bool tr_announce_list::save(std::string const& torrent_file, tr_error** error) const
{ {
// load the .torrent file // load the torrent file
auto metainfo = tr_variant{}; auto metainfo = tr_variant{};
if (!tr_variantFromFile(&metainfo, TR_VARIANT_PARSE_BENC, std::string{ torrent_file }, error)) if (!tr_variantFromFile(&metainfo, TR_VARIANT_PARSE_BENC, std::string{ torrent_file }, error))
{ {

View File

@ -101,7 +101,7 @@ bool tr_metaInfoBuilderSetPieceSize(tr_metainfo_builder* builder, uint32_t bytes
void tr_metaInfoBuilderFree(tr_metainfo_builder*); void tr_metaInfoBuilderFree(tr_metainfo_builder*);
/** /**
* @brief create a new .torrent file * @brief create a new torrent file
* *
* This is actually done in a worker thread, not the main thread! * This is actually done in a worker thread, not the main thread!
* Otherwise the client's interface would lock up while this runs. * Otherwise the client's interface would lock up while this runs.

View File

@ -27,7 +27,7 @@ struct tr_session;
*/ */
void tr_setConfigDir(tr_session* session, std::string_view config_dir); void tr_setConfigDir(tr_session* session, std::string_view config_dir);
/** @brief return the directory where .torrent files are stored */ /** @brief return the directory where torrent files are stored */
char const* tr_getTorrentDir(tr_session const*); char const* tr_getTorrentDir(tr_session const*);
/** @brief return the directory where the Web Client's web ui files are kept */ /** @brief return the directory where the Web Client's web ui files are kept */

View File

@ -257,7 +257,7 @@ static bool useNewMetainfo(tr_torrent* tor, tr_incomplete_metadata const* m, tr_
return false; return false;
} }
// yay we have an info dict. Let's make a .torrent file // yay we have an info dict. Let's make a torrent file
auto top_v = tr_variant{}; auto top_v = tr_variant{};
tr_buildMetainfoExceptInfoDict(tor->metainfo_, &top_v); tr_buildMetainfoExceptInfoDict(tor->metainfo_, &top_v);
tr_variantMergeDicts(tr_variantDictAddDict(&top_v, TR_KEY_info, 0), &info_dict_v); tr_variantMergeDicts(tr_variantDictAddDict(&top_v, TR_KEY_info, 0), &info_dict_v);
@ -265,7 +265,7 @@ static bool useNewMetainfo(tr_torrent* tor, tr_incomplete_metadata const* m, tr_
tr_variantFree(&top_v); tr_variantFree(&top_v);
tr_variantFree(&info_dict_v); tr_variantFree(&info_dict_v);
// does this synthetic .torrent file parse? // does this synthetic torrent file parse?
auto metainfo = tr_torrent_metainfo{}; auto metainfo = tr_torrent_metainfo{};
if (!metainfo.parseBenc(benc)) if (!metainfo.parseBenc(benc))
{ {

View File

@ -234,7 +234,7 @@ void tr_sessionClose(tr_session*);
/** /**
* @brief Return the session's configuration directory. * @brief Return the session's configuration directory.
* *
* This is where transmission stores its .torrent files, .resume files, * This is where transmission stores its torrent files, .resume files,
* blocklists, etc. It's set in tr_transmissionInit() and is immutable * blocklists, etc. It's set in tr_transmissionInit() and is immutable
* during the session. * during the session.
*/ */
@ -780,7 +780,7 @@ char const* tr_blocklistGetURL(tr_session const*);
/** @} */ /** @} */
/** /**
* Instantiating tr_torrents and wrangling .torrent file metadata * Instantiating tr_torrents and wrangling torrent file metadata
* *
* 1. Torrent metadata is handled in the tr_torrent_metadata class. * 1. Torrent metadata is handled in the tr_torrent_metadata class.
* *
@ -803,7 +803,7 @@ tr_ctor* tr_ctorNew(tr_session const* session);
/** @brief Free a torrent constructor object */ /** @brief Free a torrent constructor object */
void tr_ctorFree(tr_ctor* ctor); void tr_ctorFree(tr_ctor* ctor);
/** @brief Set whether or not to delete the source .torrent file /** @brief Set whether or not to delete the source torrent file
when the torrent is added. (Default: False) */ when the torrent is added. (Default: False) */
void tr_ctorSetDeleteSource(tr_ctor* ctor, bool doDelete); void tr_ctorSetDeleteSource(tr_ctor* ctor, bool doDelete);
@ -813,7 +813,7 @@ bool tr_ctorSetMetainfoFromMagnetLink(tr_ctor* ctor, char const* magnet, tr_erro
/** @brief Set the constructor's metainfo from a raw benc already in memory */ /** @brief Set the constructor's metainfo from a raw benc already in memory */
bool tr_ctorSetMetainfo(tr_ctor* ctor, char const* metainfo, size_t len, tr_error** error); bool tr_ctorSetMetainfo(tr_ctor* ctor, char const* metainfo, size_t len, tr_error** error);
/** @brief Set the constructor's metainfo from a local .torrent file */ /** @brief Set the constructor's metainfo from a local torrent file */
bool tr_ctorSetMetainfoFromFile(tr_ctor* ctor, char const* filename, tr_error** error); bool tr_ctorSetMetainfoFromFile(tr_ctor* ctor, char const* filename, tr_error** error);
tr_torrent_metainfo const* tr_ctorGetMetainfo(tr_ctor const* ctor); tr_torrent_metainfo const* tr_ctorGetMetainfo(tr_ctor const* ctor);
@ -859,10 +859,10 @@ bool tr_ctorGetPaused(tr_ctor const* ctor, tr_ctorMode mode, bool* setmeIsPaused
/** @brief Get the download path from this peer constructor */ /** @brief Get the download path from this peer constructor */
bool tr_ctorGetDownloadDir(tr_ctor const* ctor, tr_ctorMode mode, char const** setmeDownloadDir); bool tr_ctorGetDownloadDir(tr_ctor const* ctor, tr_ctorMode mode, char const** setmeDownloadDir);
/** @brief Get the "delete .torrent file" flag from this peer constructor */ /** @brief Get the "delete torrent file" flag from this peer constructor */
bool tr_ctorGetDeleteSource(tr_ctor const* ctor, bool* setmeDoDelete); bool tr_ctorGetDeleteSource(tr_ctor const* ctor, bool* setmeDoDelete);
/** @brief Get the .torrent file that this ctor's metainfo came from, /** @brief Get the torrent file that this ctor's metainfo came from,
or nullptr if tr_ctorSetMetainfoFromFile() wasn't used */ or nullptr if tr_ctorSetMetainfoFromFile() wasn't used */
char const* tr_ctorGetSourceFile(tr_ctor const* ctor); char const* tr_ctorGetSourceFile(tr_ctor const* ctor);
@ -897,7 +897,7 @@ tr_torrent* tr_torrentNew(tr_ctor* ctor, tr_torrent** setme_duplicate_of);
using tr_fileFunc = bool (*)(char const* filename, struct tr_error** error); using tr_fileFunc = bool (*)(char const* filename, struct tr_error** error);
/** @brief Removes our .torrent and .resume files for this torrent */ /** @brief Removes our torrent and .resume files for this torrent */
void tr_torrentRemove(tr_torrent* torrent, bool removeLocalData, tr_fileFunc removeFunc); void tr_torrentRemove(tr_torrent* torrent, bool removeLocalData, tr_fileFunc removeFunc);
/** @brief Start a torrent */ /** @brief Start a torrent */
@ -1443,7 +1443,7 @@ struct tr_torrent_view
struct tr_torrent_view tr_torrentView(tr_torrent const* tor); struct tr_torrent_view tr_torrentView(tr_torrent const* tor);
/* /*
* Get the filename of Transmission's internal copy of the .torrent file. * Get the filename of Transmission's internal copy of the torrent file.
* This is a duplicate that must be freed with tr_free() when done. * This is a duplicate that must be freed with tr_free() when done.
*/ */
char* tr_torrentFilename(tr_torrent const* tor); char* tr_torrentFilename(tr_torrent const* tor);
@ -1538,7 +1538,7 @@ struct tr_stat
float percentComplete; float percentComplete;
/** How much of the metadata the torrent has. /** How much of the metadata the torrent has.
For torrents added from a .torrent this will always be 1. For torrents added from a torrent this will always be 1.
For magnet links, this number will from from 0 to 1 as the metadata is downloaded. For magnet links, this number will from from 0 to 1 as the metadata is downloaded.
Range is [0..1] */ Range is [0..1] */
float metadataPercentComplete; float metadataPercentComplete;

View File

@ -158,7 +158,7 @@ static void tr_watchdir_on_retry_timer(evutil_socket_t /*fd*/, short /*type*/, v
return; return;
} }
tr_logAddWarn(fmt::format(_("Couldn't add .torrent file '{path}'"), fmt::arg("path", retry->name))); tr_logAddWarn(fmt::format(_("Couldn't add torrent file '{path}'"), fmt::arg("path", retry->name)));
} }
tr_watchdir_retries_remove(&handle->active_retries, retry); tr_watchdir_retries_remove(&handle->active_retries, retry);

View File

@ -69,7 +69,7 @@ void WatchDir::setPath(QString const& path, bool is_enabled)
{ {
watcher_ = std::make_unique<QFileSystemWatcher>(QStringList{ path }); watcher_ = std::make_unique<QFileSystemWatcher>(QStringList{ path });
connect(watcher_.get(), &QFileSystemWatcher::directoryChanged, this, &WatchDir::watcherActivated); connect(watcher_.get(), &QFileSystemWatcher::directoryChanged, this, &WatchDir::watcherActivated);
// trigger the watchdir for .torrent files in there already // trigger the watchdir for torrent files in there already
QTimer::singleShot(0, this, SLOT(rescanAllWatchedDirectories())); QTimer::singleShot(0, this, SLOT(rescanAllWatchedDirectories()));
} }
} }
@ -86,7 +86,7 @@ void WatchDir::watcherActivated(QString const& path)
files.insert(str); files.insert(str);
} }
// try to add any new files which end in .torrent // try to add any new files which end in torrent
auto const new_files = files - watch_dir_files_; auto const new_files = files - watch_dir_files_;
auto const torrent_suffix = QStringLiteral(".torrent"); auto const torrent_suffix = QStringLiteral(".torrent");
@ -107,7 +107,7 @@ void WatchDir::watcherActivated(QString const& path)
case ERROR: case ERROR:
{ {
// give the .torrent a few seconds to finish downloading // give the torrent a few seconds to finish downloading
auto* t = new QTimer(this); auto* t = new QTimer(this);
t->setObjectName(dir.absoluteFilePath(name)); t->setObjectName(dir.absoluteFilePath(name));
t->setSingleShot(true); t->setSingleShot(true);

View File

@ -338,7 +338,7 @@ TEST_F(AnnounceListTest, save)
}; };
auto constexpr Tiers = std::array<tr_tracker_tier_t, 3>{ 0, 1, 2 }; auto constexpr Tiers = std::array<tr_tracker_tier_t, 3>{ 0, 1, 2 };
// first, set up a scratch .torrent // first, set up a scratch torrent
auto constexpr* const OriginalFile = LIBTRANSMISSION_TEST_ASSETS_DIR "/Android-x86 8.1 r6 iso.torrent"; auto constexpr* const OriginalFile = LIBTRANSMISSION_TEST_ASSETS_DIR "/Android-x86 8.1 r6 iso.torrent";
auto original_content = std::vector<char>{}; auto original_content = std::vector<char>{};
auto const test_file = tr_strvJoin(::testing::TempDir(), "transmission-announce-list-test.torrent"sv); auto const test_file = tr_strvJoin(::testing::TempDir(), "transmission-announce-list-test.torrent"sv);
@ -354,13 +354,13 @@ TEST_F(AnnounceListTest, save)
EXPECT_TRUE(announce_list.add(Urls[1], Tiers[1])); EXPECT_TRUE(announce_list.add(Urls[1], Tiers[1]));
EXPECT_TRUE(announce_list.add(Urls[2], Tiers[2])); EXPECT_TRUE(announce_list.add(Urls[2], Tiers[2]));
// try saving to a nonexistent .torrent file // try saving to a nonexistent torrent file
EXPECT_FALSE(announce_list.save("/this/path/does/not/exist", &error)); EXPECT_FALSE(announce_list.save("/this/path/does/not/exist", &error));
EXPECT_NE(nullptr, error); EXPECT_NE(nullptr, error);
EXPECT_NE(0, error->code); EXPECT_NE(0, error->code);
tr_error_clear(&error); tr_error_clear(&error);
// now save to a real .torrent fi le // now save to a real torrent file
EXPECT_TRUE(announce_list.save(test_file, &error)); EXPECT_TRUE(announce_list.save(test_file, &error));
EXPECT_EQ(nullptr, error); EXPECT_EQ(nullptr, error);

View File

@ -56,8 +56,8 @@ protected:
EXPECT_FALSE(builder->isFolder); EXPECT_FALSE(builder->isFolder);
EXPECT_FALSE(builder->abortFlag); EXPECT_FALSE(builder->abortFlag);
// have tr_makeMetaInfo() build the .torrent file // have tr_makeMetaInfo() build the torrent file
auto const torrent_file = tr_strvJoin(input_file, ".torrent"); auto const torrent_file = tr_strvJoin(input_file, ".torrent"sv);
tr_makeMetaInfo( tr_makeMetaInfo(
builder, builder,
torrent_file.c_str(), torrent_file.c_str(),
@ -80,7 +80,7 @@ protected:
} }
sync(); sync();
// now let's check our work: parse the .torrent file // now let's check our work: parse the torrent file
EXPECT_TRUE(metainfo.parseTorrentFile(torrent_file)); EXPECT_TRUE(metainfo.parseTorrentFile(torrent_file));
// quick check of some of the parsed metainfo // quick check of some of the parsed metainfo
@ -147,7 +147,7 @@ protected:
EXPECT_EQ(payload_sizes[i], builder->files[i].size); EXPECT_EQ(payload_sizes[i], builder->files[i].size);
} }
// build the .torrent file // build the torrent file
auto torrent_file = tr_strvJoin(top, ".torrent"sv); auto torrent_file = tr_strvJoin(top, ".torrent"sv);
tr_makeMetaInfo( tr_makeMetaInfo(
builder, builder,
@ -171,7 +171,7 @@ protected:
EXPECT_TRUE(waitFor(test, 5000)); EXPECT_TRUE(waitFor(test, 5000));
sync(); sync();
// now let's check our work: parse the .torrent file // now let's check our work: parse the torrent file
auto metainfo = tr_torrent_metainfo{}; auto metainfo = tr_torrent_metainfo{};
EXPECT_TRUE(metainfo.parseTorrentFile(torrent_file)); EXPECT_TRUE(metainfo.parseTorrentFile(torrent_file));

View File

@ -38,6 +38,8 @@ protected:
SessionTest::SetUp(); SessionTest::SetUp();
} }
static auto constexpr MaxWaitMsec = 3000;
}; };
TEST_P(IncompleteDirTest, incompleteDir) TEST_P(IncompleteDirTest, incompleteDir)
@ -110,7 +112,7 @@ TEST_P(IncompleteDirTest, incompleteDir)
{ {
return data.done; return data.done;
}; };
EXPECT_TRUE(waitFor(test, 1000)); EXPECT_TRUE(waitFor(test, MaxWaitMsec));
} }
evbuffer_free(data.buf); evbuffer_free(data.buf);

View File

@ -199,7 +199,7 @@ TEST_F(RenameTest, singleFilenameTorrent)
EXPECT_STREQ("foobar", tr_torrentName(tor)); // confirm the torrent's name is now 'foobar' EXPECT_STREQ("foobar", tr_torrentName(tor)); // confirm the torrent's name is now 'foobar'
EXPECT_STREQ("foobar", tr_torrentFile(tor, 0).name); // confirm the file's name is now 'foobar' EXPECT_STREQ("foobar", tr_torrentFile(tor, 0).name); // confirm the file's name is now 'foobar'
char* const torrent_filename = tr_torrentFilename(tor); char* const torrent_filename = tr_torrentFilename(tor);
EXPECT_STREQ(nullptr, strstr(torrent_filename, "foobar")); // confirm .torrent file hasn't changed EXPECT_STREQ(nullptr, strstr(torrent_filename, "foobar")); // confirm torrent file hasn't changed
tr_free(torrent_filename); tr_free(torrent_filename);
tmpstr = tr_strvPath(tor->currentDir().sv(), "foobar"); tmpstr = tr_strvPath(tor->currentDir().sv(), "foobar");
EXPECT_TRUE(tr_sys_path_exists(tmpstr.c_str(), nullptr)); // confirm the file's name is now 'foobar' on the disk EXPECT_TRUE(tr_sys_path_exists(tmpstr.c_str(), nullptr)); // confirm the file's name is now 'foobar' on the disk

View File

@ -36,7 +36,7 @@ namespace
auto constexpr TimeoutSecs = long{ 30 }; auto constexpr TimeoutSecs = long{ 30 };
char constexpr MyName[] = "transmission-show"; char constexpr MyName[] = "transmission-show";
char constexpr Usage[] = "Usage: transmission-show [options] <.torrent file>"; char constexpr Usage[] = "Usage: transmission-show [options] <torrent-file>";
char constexpr UserAgent[] = "transmission-show/" LONG_VERSION_STRING; char constexpr UserAgent[] = "transmission-show/" LONG_VERSION_STRING;
auto options = std::array<tr_option, 5>{ auto options = std::array<tr_option, 5>{
@ -328,13 +328,13 @@ int tr_main(int argc, char* argv[])
/* make sure the user specified a filename */ /* make sure the user specified a filename */
if (std::empty(opts.filename)) if (std::empty(opts.filename))
{ {
fprintf(stderr, "ERROR: No .torrent file specified.\n"); fprintf(stderr, "ERROR: No torrent file specified.\n");
tr_getopt_usage(MyName, Usage, std::data(options)); tr_getopt_usage(MyName, Usage, std::data(options));
fprintf(stderr, "\n"); fprintf(stderr, "\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
/* try to parse the .torrent file */ /* try to parse the torrent file */
auto metainfo = tr_torrent_metainfo{}; auto metainfo = tr_torrent_metainfo{};
tr_error* error = nullptr; tr_error* error = nullptr;
auto const parsed = metainfo.parseTorrentFile(opts.filename, nullptr, &error); auto const parsed = metainfo.parseTorrentFile(opts.filename, nullptr, &error);
@ -342,7 +342,7 @@ int tr_main(int argc, char* argv[])
{ {
fprintf( fprintf(
stderr, stderr,
"Error parsing .torrent file \"%" TR_PRIsv "\": %s (%d)\n", "Error parsing torrent file \"%" TR_PRIsv "\": %s (%d)\n",
TR_PRIsv_ARG(opts.filename), TR_PRIsv_ARG(opts.filename),
error->message, error->message,
error->code); error->code);