Replace wxString::Format with agi::format

It's modestly faster, significantly more type-safe, and doesn't assert
when there's too few arguments, which causes problems for plural forms.

Closes #1733.
This commit is contained in:
Thomas Goyne 2014-05-29 08:28:37 -07:00
parent 8d26c66d0f
commit 37c02ae127
50 changed files with 421 additions and 171 deletions

View file

@ -55,6 +55,8 @@
<ClInclude Include="$(SrcDir)include\libaegisub\exception.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\file_mapping.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\format.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\format_flyweight.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\format_path.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\fs.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\fs_fwd.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\hotkey.h" />

View file

@ -173,6 +173,12 @@
<ClInclude Include="$(SrcDir)include\libaegisub\format.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\format_flyweight.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\format_path.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\character_count.h">
<Filter>Header Files</Filter>
</ClInclude>

View file

@ -42,6 +42,7 @@
<ClCompile Include="$(SrcDir)tests\cajun.cpp" />
<ClCompile Include="$(SrcDir)tests\color.cpp" />
<ClCompile Include="$(SrcDir)tests\dialogue_lexer.cpp" />
<ClCompile Include="$(SrcDir)tests\format.cpp" />
<ClCompile Include="$(SrcDir)tests\fs.cpp" />
<ClCompile Include="$(SrcDir)tests\hotkey.cpp" />
<ClCompile Include="$(SrcDir)tests\iconv.cpp" />

View file

@ -14,11 +14,15 @@
//
// Aegisub Project http://www.aegisub.org/
#include <libaegisub/fs_fwd.h>
#include <boost/interprocess/streams/vectorstream.hpp>
#include <boost/io/ios_state.hpp>
#include <cassert>
#include <codecvt>
#include <type_traits>
class wxString;
namespace agi { namespace format_detail {
// A static cast which throws at runtime if the cast is invalid rather than
// failing to compile, as with format strings we don't know what type to cast
@ -40,36 +44,75 @@ Out runtime_cast(In const& value) {
return runtime_cast_helper<In, Out>::cast(value);
}
template<typename T>
void write_string(std::ostream& out, int, T const& value) {
out << value;
}
// Check length for string types
inline void write_string(std::ostream& out, int max_len, const char *value) {
if (max_len <= 0)
out << value;
else {
std::streamsize len = 0;
for (; len < max_len && value[len]; ++len) ;
out.write(value, len);
}
template<typename Char>
int actual_len(int max_len, const Char *value) {
int len = 0;
while (value[len] && (max_len <= 0 || len < max_len)) ++len;
return len;
}
inline void write_string(std::ostream& out, int max_len, std::string const& value) {
template<typename Char>
int actual_len(int max_len, std::basic_string<Char> value) {
if (max_len > 0 && static_cast<size_t>(max_len) < value.size())
out.write(value.data(), max_len);
else
out << value;
return max_len;
return value.size();
}
template<typename Char>
inline void do_write_str(std::basic_ostream<Char>& out, const Char *str, int len) {
out.write(str, len);
}
inline void do_write_str(std::ostream& out, const wchar_t *str, int len) {
std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
auto u8 = utf8_conv.to_bytes(str, str + len);
out.write(u8.data(), u8.size());
}
inline void do_write_str(std::wostream& out, const char *str, int len) {
std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
auto wc = utf8_conv.from_bytes(str, str + len);
out.write(wc.data(), wc.size());
}
}
template<typename Char, typename T>
struct writer {
static void write(std::basic_ostream<Char>& out, int, T const& value) {
out << value;
}
};
// Ensure things with specializations don't get implicitly initialized
template<> struct writer<char, agi::fs::path>;
template<> struct writer<wchar_t, agi::fs::path>;
template<> struct writer<char, wxString>;
template<> struct writer<wchar_t, wxString>;
template<typename StreamChar, typename Char>
struct writer<StreamChar, const Char *> {
static void write(std::basic_ostream<StreamChar>& out, int max_len, const Char *value) {
format_detail::do_write_str(out, value, format_detail::actual_len(max_len, value));
}
};
template<typename StreamChar, typename Char>
struct writer<StreamChar, std::basic_string<Char>> {
static void write(std::basic_ostream<StreamChar>& out, int max_len, std::basic_string<Char> const& value) {
format_detail::do_write_str(out, value.data(), format_detail::actual_len(max_len, value));
}
};
namespace format_detail {
template<typename Char>
class formatter {
formatter(const formatter&) = delete;
formatter& operator=(const formatter&) = delete;
std::ostream& out;
const char *fmt;
const char *fmt_cur = nullptr;
std::basic_ostream<Char>& out;
const Char *fmt;
const Char *fmt_cur = nullptr;
bool read_width = false;
bool read_precision = false;
@ -78,7 +121,7 @@ class formatter {
int width = 0;
int precision = 0;
boost::io::ios_all_saver saver;
boost::io::basic_ios_all_saver<Char> saver;
void read_and_append_up_to_next_specifier() {
for (std::streamsize len = 0; ; ++len) {
@ -168,7 +211,7 @@ class formatter {
void parse_length_modifiers() {
// Where "parse" means "skip" since we don't need them
for (char c = *fmt_cur;
for (Char c = *fmt_cur;
c == 'l' || c == 'h' || c == 'L' || c == 'j' || c == 'z' || c == 't';
c = *++fmt_cur);
}
@ -198,7 +241,7 @@ class formatter {
}
public:
formatter(std::ostream& out, const char *fmt) : out(out), fmt(fmt), saver(out) { }
formatter(std::basic_ostream<Char>& out, const Char *fmt) : out(out), fmt(fmt), saver(out) { }
~formatter() {
// Write remaining formatting string
for (std::streamsize len = 0; ; ++len) {
@ -245,7 +288,7 @@ public:
out.width(width);
out.precision(precision < 0 ? 6 : precision);
char c = *fmt_cur ? fmt_cur[0] : 's';
Char c = *fmt_cur ? fmt_cur[0] : 's';
if (c >= 'A' && c <= 'Z') {
out.setf(std::ios::uppercase);
c += 'a' - 'A';
@ -254,7 +297,7 @@ public:
switch (c) {
case 'c':
out.setf(std::ios::dec, std::ios::basefield);
out << runtime_cast<char>(value);
out << runtime_cast<Char>(value);
break;
case 'd': case 'i':
out.setf(std::ios::dec, std::ios::basefield);
@ -292,7 +335,7 @@ public:
break;
default: // s and other
out.setf(std::ios::boolalpha);
write_string(out, precision, value);
writer<Char, typename std::decay<T>::type>::write(out, precision, value);
break;
}
@ -301,23 +344,24 @@ public:
};
// Base case for variadic template recursion
inline void format(formatter&&) { }
template<typename Char>
inline void format(formatter<Char>&&) { }
template<typename T, typename... Args>
void format(formatter&& fmt, T&& first, Args&&... rest) {
template<typename Char, typename T, typename... Args>
void format(formatter<Char>&& fmt, T&& first, Args&&... rest) {
fmt(first);
format(std::move(fmt), std::forward<Args>(rest)...);
}
} // namespace format_detail
template<typename... Args>
void format(std::ostream& out, const char *fmt, Args&&... args) {
format(format_detail::formatter(out, fmt), std::forward<Args>(args)...);
template<typename Char, typename... Args>
void format(std::basic_ostream<Char>& out, const Char *fmt, Args&&... args) {
format(format_detail::formatter<Char>(out, fmt), std::forward<Args>(args)...);
}
template<typename... Args>
std::string format(const char *fmt, Args&&... args) {
boost::interprocess::basic_vectorstream<std::string> out;
template<typename Char, typename... Args>
std::basic_string<Char> format(const Char *fmt, Args&&... args) {
boost::interprocess::basic_vectorstream<std::basic_string<Char>> out;
format(out, fmt, std::forward<Args>(args)...);
return out.vector();
}

View file

@ -0,0 +1,36 @@
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include <libaegisub/fs_fwd.h>
#include <boost/flyweight.hpp>
namespace agi {
template<>
struct writer<char, boost::flyweight<std::string>> {
static void write(std::basic_ostream<char>& out, int max_len, boost::flyweight<std::string> const& value) {
writer<char, std::string>::write(out, max_len, value.get());
}
};
template<>
struct writer<wchar_t, boost::flyweight<std::string>> {
static void write(std::basic_ostream<wchar_t>& out, int max_len, boost::flyweight<std::string> const& value) {
writer<wchar_t, std::string>::write(out, max_len, value.get());
}
};
}

View file

@ -0,0 +1,36 @@
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include <libaegisub/fs_fwd.h>
#include <boost/filesystem/path.hpp>
namespace agi {
// Default version quotes the path
template<>
struct writer<char, agi::fs::path> {
static void write(std::basic_ostream<char>& out, int max_len, agi::fs::path const& value) {
out << value.string();
}
};
template<>
struct writer<wchar_t, agi::fs::path> {
static void write(std::basic_ostream<wchar_t>& out, int max_len, agi::fs::path const& value) {
out << value.wstring();
}
};
}

View file

@ -13,7 +13,7 @@ maybe_append() {
}
find ../src ../src/command -name \*.cpp -o -name \*.h \
| xgettext --files-from=- -o - --c++ -k_ -kSTR_MENU -kSTR_DISP -kSTR_HELP -kwxT -kwxPLURAL:1,2 \
| xgettext --files-from=- -o - --c++ -k_ -kSTR_MENU -kSTR_DISP -kSTR_HELP -kfmt_tl -kfmt_plural:2,3 -kwxT -kwxPLURAL:1,2 \
| sed 's/SOME DESCRIPTIVE TITLE./Aegisub 3.2/' \
| sed 's/YEAR/2005-2014/' \
| sed "s/THE PACKAGE'S COPYRIGHT HOLDER/Rodrigo Braz Monteiro, Niels Martin Hansen, Thomas Goyne et. al./" \

View file

@ -72,6 +72,7 @@
// Common C++
#include <algorithm>
#include <array>
#include <codecvt>
#include <functional>
#include <iterator>
#include <limits>
@ -81,9 +82,10 @@
#include <set>
#include <string>
#include <type_traits>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <unordered_map>
#include <utility>
#include <vector>
#ifdef _MSC_VER
@ -100,6 +102,7 @@
// Boost
#include <boost/container/list.hpp>
#include <boost/flyweight.hpp>
#include <boost/io/ios_state.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/algorithm.hpp>
@ -109,6 +112,7 @@
#include <boost/filesystem/path.hpp>
#undef BOOST_NO_SCOPED_ENUMS
#include <boost/interprocess/streams/bufferstream.hpp>
#include <boost/interprocess/streams/vectorstream.hpp>
// wxWidgets headers
#include <wx/defs.h> // Leave this first.

View file

@ -37,6 +37,7 @@
#include "audio_renderer_waveform.h"
#include "audio_timing.h"
#include "compat.h"
#include "format.h"
#include "include/aegisub/audio_provider.h"
#include "include/aegisub/context.h"
#include "include/aegisub/hotkey.h"
@ -417,21 +418,21 @@ public:
if (changed_hour)
{
time_string = wxString::Format("%d:%02d:", mark_hour, mark_minute);
time_string = fmt_wx("%d:%02d:", mark_hour, mark_minute);
last_hour = mark_hour;
last_minute = mark_minute;
}
else if (changed_minute)
{
time_string = wxString::Format("%d:", mark_minute);
time_string = fmt_wx("%d:", mark_minute);
last_minute = mark_minute;
}
if (scale_minor >= Sc_Decisecond)
time_string += wxString::Format("%02d", (int)mark_second);
time_string += fmt_wx("%02d", mark_second);
else if (scale_minor == Sc_Centisecond)
time_string += wxString::Format("%02.1f", mark_second);
time_string += fmt_wx("%02.1f", mark_second);
else
time_string += wxString::Format("%02.2f", mark_second);
time_string += fmt_wx("%02.2f", mark_second);
int tw, th;
dc.GetTextExtent(time_string, &tw, &th);
@ -673,7 +674,7 @@ wxString AudioDisplay::GetZoomLevelDescription(int level) const
const int base_pixels_per_second = 50; /// @todo Make this customisable along with the above
const int second_pixels = 100 * base_pixels_per_second / factor;
return wxString::Format(_("%d%%, %d pixel/second"), factor, second_pixels);
return fmt_tl("%d%%, %d pixel/second", factor, second_pixels);
}
int AudioDisplay::GetZoomLevelFactor(int level)

View file

@ -15,6 +15,7 @@
#include "command.h"
#include "../compat.h"
#include "../format.h"
#include <libaegisub/log.h>
@ -27,7 +28,7 @@ namespace cmd {
static iterator find_command(std::string const& name) {
auto it = cmd_map.find(name);
if (it == cmd_map.end())
throw CommandNotFound(from_wx(wxString::Format(_("'%s' is not a valid command name"), to_wx(name))));
throw CommandNotFound(agi::format(_("'%s' is not a valid command name"), name));
return it;
}

View file

@ -38,6 +38,7 @@
#include "../compat.h"
#include "../dialog_search_replace.h"
#include "../dialogs.h"
#include "../format.h"
#include "../include/aegisub/context.h"
#include "../initial_line_state.h"
#include "../libresrc/libresrc.h"
@ -50,7 +51,6 @@
#include "../video_controller.h"
#include <libaegisub/address_of_adaptor.h>
#include <libaegisub/format.h>
#include <libaegisub/of_type_adaptor.h>
#include <libaegisub/make_unique.h>
@ -1146,12 +1146,12 @@ struct edit_redo final : public Command {
wxString StrMenu(const agi::Context *c) const override {
return c->subsController->IsRedoStackEmpty() ?
_("Nothing to &redo") :
wxString::Format(_("&Redo %s"), c->subsController->GetRedoDescription());
fmt_tl("&Redo %s", c->subsController->GetRedoDescription());
}
wxString StrDisplay(const agi::Context *c) const override {
return c->subsController->IsRedoStackEmpty() ?
_("Nothing to redo") :
wxString::Format(_("Redo %s"), c->subsController->GetRedoDescription());
fmt_tl("Redo %s", c->subsController->GetRedoDescription());
}
bool Validate(const agi::Context *c) override {
@ -1172,12 +1172,12 @@ struct edit_undo final : public Command {
wxString StrMenu(const agi::Context *c) const override {
return c->subsController->IsUndoStackEmpty() ?
_("Nothing to &undo") :
wxString::Format(_("&Undo %s"), c->subsController->GetUndoDescription());
fmt_tl("&Undo %s", c->subsController->GetUndoDescription());
}
wxString StrDisplay(const agi::Context *c) const override {
return c->subsController->IsUndoStackEmpty() ?
_("Nothing to undo") :
wxString::Format(_("Undo %s"), c->subsController->GetUndoDescription());
fmt_tl("Undo %s", c->subsController->GetUndoDescription());
}
bool Validate(const agi::Context *c) override {

View file

@ -38,6 +38,7 @@
#include "../dialog_detached_video.h"
#include "../dialog_manager.h"
#include "../dialogs.h"
#include "../format.h"
#include "../frame_main.h"
#include "../include/aegisub/context.h"
#include "../include/aegisub/subtitles_provider.h"
@ -50,7 +51,6 @@
#include "../video_display.h"
#include "../video_frame.h"
#include <libaegisub/format.h>
#include <libaegisub/fs.h>
#include <libaegisub/path.h>
#include <libaegisub/make_unique.h>
@ -232,7 +232,7 @@ struct video_cycle_subtitles_provider final : public cmd::Command {
if (it == end(providers)) it = begin(providers);
OPT_SET("Subtitle/Provider")->SetString(*it);
c->frame->StatusTimeout(wxString::Format(_("Subtitles provider set to %s"), to_wx(*it)), 5000);
c->frame->StatusTimeout(fmt_tl("Subtitles provider set to %s", *it), 5000);
}
};

View file

@ -28,6 +28,7 @@
// Aegisub Project http://www.aegisub.org/
#include "libresrc/libresrc.h"
#include "format.h"
#include "version.h"
#include <wx/button.h>
@ -126,7 +127,7 @@ struct AboutScreen : wxDialog {
aboutString += translatorCredit;
aboutString += "\n" + libString;
aboutString += _("\nSee the help file for full credits.\n");
aboutString += wxString::Format(_("Built by %s on %s."), GetAegisubBuildCredit(), GetAegisubBuildTime());
aboutString += fmt_tl("Built by %s on %s.", GetAegisubBuildCredit(), GetAegisubBuildTime());
// Replace copyright symbol
wxChar copySymbol = 0xA9;

View file

@ -31,6 +31,7 @@
#include "compat.h"
#include "command/command.h"
#include "dialog_manager.h"
#include "format.h"
#include "help_button.h"
#include "include/aegisub/context.h"
#include "libresrc/libresrc.h"
@ -273,20 +274,20 @@ void DialogAutomation::OnInfo(wxCommandEvent &)
wxArrayString info;
std::back_insert_iterator<wxArrayString> append_info(info);
info.push_back(wxString::Format(
_("Total scripts loaded: %d\nGlobal scripts loaded: %d\nLocal scripts loaded: %d\n"),
(int)local_manager->GetScripts().size() + (int)global_manager->GetScripts().size(),
(int)global_manager->GetScripts().size(),
(int)local_manager->GetScripts().size()));
info.push_back(fmt_tl(
"Total scripts loaded: %d\nGlobal scripts loaded: %d\nLocal scripts loaded: %d\n",
local_manager->GetScripts().size() + global_manager->GetScripts().size(),
global_manager->GetScripts().size(),
local_manager->GetScripts().size()));
info.push_back(_("Scripting engines installed:"));
boost::transform(Automation4::ScriptFactory::GetFactories(), append_info,
[](std::unique_ptr<Automation4::ScriptFactory> const& f) {
return wxString::Format("- %s (%s)", to_wx(f->GetEngineName()), to_wx(f->GetFilenamePattern()));
return fmt_wx("- %s (%s)", f->GetEngineName(), f->GetFilenamePattern());
});
if (ei) {
info.push_back(wxString::Format(_("\nScript info:\nName: %s\nDescription: %s\nAuthor: %s\nVersion: %s\nFull path: %s\nState: %s\n\nFeatures provided by script:"),
info.push_back(fmt_tl("\nScript info:\nName: %s\nDescription: %s\nAuthor: %s\nVersion: %s\nFull path: %s\nState: %s\n\nFeatures provided by script:",
ei->script->GetName(),
ei->script->GetDescription(),
ei->script->GetAuthor(),
@ -295,10 +296,10 @@ void DialogAutomation::OnInfo(wxCommandEvent &)
ei->script->GetLoadedState() ? _("Correctly loaded") : _("Failed to load")));
boost::transform(ei->script->GetMacros(), append_info, [=](const cmd::Command *f) {
return wxString::Format(_(" Macro: %s (%s)"), f->StrDisplay(context), to_wx(f->name()));
return fmt_tl(" Macro: %s (%s)", f->StrDisplay(context), f->name());
});
boost::transform(ei->script->GetFilters(), append_info, [](const Automation4::ExportFilter* f) {
return wxString::Format(_(" Export filter: %s"), to_wx(f->GetName()));
return fmt_tl(" Export filter: %s", f->GetName());
});
}

View file

@ -15,6 +15,7 @@
// Aegisub Project http://www.aegisub.org/
#include "compat.h"
#include "format.h"
#include "libresrc/libresrc.h"
#include "options.h"
@ -140,7 +141,7 @@ void DialogAutosave::Populate(std::map<wxString, AutosaveFile> &files_map, std::
auto it = files_map.find(name);
if (it == files_map.end())
it = files_map.insert({name, AutosaveFile{name}}).first;
it->second.versions.push_back(Version{wxFileName(directory, fn).GetFullPath(), date, wxString::Format(name_fmt, date.Format())});
it->second.versions.push_back(Version{wxFileName(directory, fn).GetFullPath(), date, agi::wxformat(name_fmt, date.Format())});
} while (dir.GetNext(&fn));
}

View file

@ -34,6 +34,7 @@
#include "dialog_detached_video.h"
#include "format.h"
#include "include/aegisub/context.h"
#include "include/aegisub/hotkey.h"
#include "options.h"
@ -44,6 +45,7 @@
#include "video_controller.h"
#include "video_display.h"
#include <libaegisub/format_path.h>
#include <libaegisub/make_unique.h>
#include <boost/filesystem/path.hpp>
@ -61,7 +63,7 @@ DialogDetachedVideo::DialogDetachedVideo(agi::Context *context)
// Set obscure stuff
SetExtraStyle((GetExtraStyle() & ~wxWS_EX_BLOCK_EVENTS) | wxWS_EX_PROCESS_UI_UPDATES);
SetTitle(wxString::Format(_("Video: %s"), context->project->VideoName().filename().wstring()));
SetTitle(fmt_tl("Video: %s", context->project->VideoName().filename()));
old_display->Unload();
@ -129,7 +131,7 @@ void DialogDetachedVideo::OnKeyDown(wxKeyEvent &evt) {
void DialogDetachedVideo::OnVideoOpen() {
if (context->project->VideoProvider())
SetTitle(wxString::Format(_("Video: %s"), context->project->VideoName().filename().wstring()));
SetTitle(fmt_tl("Video: %s", context->project->VideoName().filename()));
else {
Close();
OPT_SET("Video/Detached/Enabled")->SetBool(true);

View file

@ -16,6 +16,7 @@
#include "ass_time.h"
#include "colour_button.h"
#include "format.h"
#include "help_button.h"
#include "libresrc/libresrc.h"
#include "options.h"
@ -158,7 +159,7 @@ void DialogDummyVideo::OnResolutionShortcut(wxCommandEvent &e) {
}
void DialogDummyVideo::UpdateLengthDisplay() {
length_display->SetLabel(wxString::Format(_("Resulting duration: %s"), AssTime(length / fps * 1000).GetAssFormated(true)));
length_display->SetLabel(fmt_tl("Resulting duration: %s", AssTime(length / fps * 1000).GetAssFormated(true)));
}
}

View file

@ -22,6 +22,7 @@
#include "dialog_export_ebu3264.h"
#include "compat.h"
#include "format.h"
#include "options.h"
#include <libaegisub/charset_conv.h>
@ -53,7 +54,7 @@ namespace {
bool TransferToWindow() override {
wxTextCtrl *ctrl = GetCtrl();
if (!ctrl) return false;
ctrl->SetValue(wxString::Format("%02d:%02d:%02d:%02d", (int)value->h, (int)value->m, (int)value->s, (int)value->f));
ctrl->SetValue(fmt_wx("%02d:%02d:%02d:%02d", value->h, value->m, value->s, value->f));
return true;
}

View file

@ -19,6 +19,7 @@
#include "compat.h"
#include "dialog_manager.h"
#include "format.h"
#include "help_button.h"
#include "include/aegisub/context.h"
#include "libresrc/libresrc.h"
@ -27,6 +28,7 @@
#include "utils.h"
#include <libaegisub/dispatch.h>
#include <libaegisub/format_path.h>
#include <libaegisub/fs.h>
#include <libaegisub/path.h>
#include <libaegisub/make_unique.h>
@ -122,7 +124,7 @@ void FontsCollectorThread(AssFile *subs, agi::fs::path const& destination, FcMod
agi::fs::CreateDirectory(destination.parent_path());
}
catch (agi::fs::FileSystemError const& e) {
AppendText(wxString::Format(_("* Failed to create directory '%s': %s.\n"),
AppendText(fmt_tl("* Failed to create directory '%s': %s.\n",
destination.parent_path().wstring(), to_wx(e.GetMessage())), 2);
collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE));
return;
@ -133,7 +135,7 @@ void FontsCollectorThread(AssFile *subs, agi::fs::path const& destination, FcMod
zip = agi::make_unique<wxZipOutputStream>(*out);
if (!out->IsOk() || !zip || !zip->IsOk()) {
AppendText(wxString::Format(_("* Failed to open %s.\n"), destination.wstring()), 2);
AppendText(fmt_tl("* Failed to open %s.\n", destination), 2);
collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE));
return;
}
@ -188,13 +190,13 @@ void FontsCollectorThread(AssFile *subs, agi::fs::path const& destination, FcMod
}
if (ret == 1)
AppendText(wxString::Format(_("* Copied %s.\n"), path.wstring()), 1);
AppendText(fmt_tl("* Copied %s.\n", path), 1);
else if (ret == 2)
AppendText(wxString::Format(_("* %s already exists on destination.\n"), path.filename().wstring()), 3);
AppendText(fmt_tl("* %s already exists on destination.\n", path.filename()), 3);
else if (ret == 3)
AppendText(wxString::Format(_("* Symlinked %s.\n"), path.wstring()), 1);
AppendText(fmt_tl("* Symlinked %s.\n", path), 1);
else {
AppendText(wxString::Format(_("* Failed to copy %s.\n"), path.wstring()), 2);
AppendText(fmt_tl("* Failed to copy %s.\n", path), 2);
allOk = false;
}
}

View file

@ -29,6 +29,7 @@
#include "ass_time.h"
#include "async_video_provider.h"
#include "format.h"
#include "include/aegisub/context.h"
#include "libresrc/libresrc.h"
#include "project.h"
@ -117,7 +118,7 @@ void DialogJumpTo::OnEditTime (wxCommandEvent &) {
long newframe = c->videoController->FrameAtTime(JumpTime->GetTime());
if (jumpframe != newframe) {
jumpframe = newframe;
JumpFrame->ChangeValue(wxString::Format("%li", jumpframe));
JumpFrame->ChangeValue(fmt_wx("%d", jumpframe));
}
}

View file

@ -29,6 +29,7 @@
#include "compat.h"
#include "dialog_manager.h"
#include "format.h"
#include "include/aegisub/context.h"
#include <libaegisub/dispatch.h>
@ -57,26 +58,26 @@ public:
#ifndef _WIN32
tm tmtime;
localtime_r(&time, &tmtime);
auto log = wxString::Format("%c %02d:%02d:%02d %-6ld <%-25s> [%s:%s:%d] %s\n",
auto log = fmt_wx("%c %02d:%02d:%02d %-6d <%-25s> [%s:%s:%d] %s\n",
agi::log::Severity_ID[sm.severity],
(int)tmtime.tm_hour,
(int)tmtime.tm_min,
(int)tmtime.tm_sec,
(long)(sm.time % 1000000000),
tmtime.tm_hour,
tmtime.tm_min,
tmtime.tm_sec,
(sm.time % 1000000000),
sm.section,
sm.file,
sm.func,
sm.line,
to_wx(sm.message));
sm.message);
#else
auto log = wxString::Format("%c %-6ld <%-25s> [%s:%s:%d] %s\n",
auto log = fmt_wx("%c %-6ld <%-25s> [%s:%s:%d] %s\n",
agi::log::Severity_ID[sm.severity],
(long)(sm.time % 1000000000),
(sm.time % 1000000000),
sm.section,
sm.file,
sm.func,
sm.line,
to_wx(sm.message));
sm.message);
#endif
if (wxIsMainThread())

View file

@ -18,6 +18,7 @@
#include "ass_file.h"
#include "compat.h"
#include "dialog_manager.h"
#include "format.h"
#include "frame_main.h"
#include "help_button.h"
#include "include/aegisub/context.h"
@ -207,14 +208,14 @@ void DialogSelection::Process(wxCommandEvent&) {
case Action::SET:
new_sel = std::move(matches);
message = (count = new_sel.size())
? wxString::Format(wxPLURAL("Selection was set to one line", "Selection was set to %u lines", count), (unsigned)count)
? fmt_plural(count, "Selection was set to one line", "Selection was set to %u lines", count)
: _("Selection was set to no lines");
break;
case Action::ADD:
boost::set_union(old_sel, matches, inserter(new_sel, new_sel.begin()));
message = (count = new_sel.size() - old_sel.size())
? wxString::Format(wxPLURAL("One line was added to selection", "%u lines were added to selection", count), (unsigned)count)
? fmt_plural(count, "One line was added to selection", "%u lines were added to selection", count)
: _("No lines were added to selection");
break;
@ -226,7 +227,7 @@ void DialogSelection::Process(wxCommandEvent&) {
boost::set_intersection(old_sel, matches, inserter(new_sel, new_sel.begin()));
sub_message:
message = (count = old_sel.size() - new_sel.size())
? wxString::Format(wxPLURAL("One line was removed from selection", "%u lines were removed from selection", count), (unsigned)count)
? fmt_plural(count, "One line was removed from selection", "%u lines were removed from selection", count)
: _("No lines were removed from selection");
break;
}

View file

@ -19,6 +19,7 @@
#include "ass_time.h"
#include "compat.h"
#include "dialog_manager.h"
#include "format.h"
#include "include/aegisub/context.h"
#include "help_button.h"
#include "libresrc/libresrc.h"
@ -92,7 +93,7 @@ static wxString get_history_string(json::Object &obj) {
wxString shift_amount(to_wx(obj["amount"]));
if (!obj["is by time"])
shift_amount = wxString::Format(_("%s frames"), shift_amount);
shift_amount = fmt_tl("%s frames", shift_amount);
wxString shift_direction = obj["is backward"] ? _("backward") : _("forward");
@ -109,7 +110,7 @@ static wxString get_history_string(json::Object &obj) {
if (sel_mode == 0)
lines = _("all");
else if (sel_mode == 2)
lines = wxString::Format(_("from %d onward"), (int)(int64_t)sel.front()["start"]);
lines = fmt_tl("from %d onward", (int64_t)sel.front()["start"]);
else {
lines += _("sel ");
for (auto it = sel.begin(); it != sel.end(); ++it) {
@ -118,13 +119,13 @@ static wxString get_history_string(json::Object &obj) {
if (beg == end)
lines += std::to_wstring(beg);
else
lines += wxString::Format("%d-%d", beg, end);
lines += fmt_wx("%d-%d", beg, end);
if (it + 1 != sel.end())
lines += ";";
}
}
return wxString::Format("%s, %s %s, %s, %s", filename, shift_amount, shift_direction, fields, lines);
return fmt_wx("%s, %s %s, %s, %s", filename, shift_amount, shift_direction, fields, lines);
}
DialogShiftTimes::DialogShiftTimes(agi::Context *context)

View file

@ -149,7 +149,7 @@ DialogStyleEditor::DialogStyleEditor(wxWindow *parent, AssStyle *style, agi::Con
};
auto spin_ctrl = [&](float value, int max_value) {
return new wxSpinCtrl(this, -1, wxString::Format("%g", value), wxDefaultPosition, wxSize(60, -1), wxSP_ARROW_KEYS, 0, max_value, value);
return new wxSpinCtrl(this, -1, std::to_wstring(value), wxDefaultPosition, wxSize(60, -1), wxSP_ARROW_KEYS, 0, max_value, value);
};
auto num_text_ctrl = [&](double *value, double min, double max, double step) -> wxSpinCtrlDouble * {

View file

@ -36,8 +36,9 @@
#include "dialog_manager.h"
#include "dialog_style_editor.h"
#include "dialogs.h"
#include "include/aegisub/context.h"
#include "format.h"
#include "help_button.h"
#include "include/aegisub/context.h"
#include "libresrc/libresrc.h"
#include "options.h"
#include "persist_location.h"
@ -214,9 +215,9 @@ wxSizer *make_edit_buttons(wxWindow *parent, wxString move_label, wxButton **mov
template<class Func>
std::string unique_name(Func name_checker, std::string const& source_name) {
if (name_checker(source_name)) {
std::string name = from_wx(wxString::Format(_("%s - Copy"), to_wx(source_name)));
std::string name = agi::format(_("%s - Copy"), source_name);
for (int i = 2; name_checker(name); ++i)
name = from_wx(wxString::Format(_("%s - Copy (%d)"), to_wx(source_name), i));
name = agi::format(_("%s - Copy (%d)"), source_name, i);
return name;
}
return source_name;
@ -244,7 +245,7 @@ void add_styles(Func1 name_checker, Func2 style_adder) {
int confirm_delete(int n, wxWindow *parent, wxString const& title) {
return wxMessageBox(
wxString::Format(wxPLURAL("Are you sure you want to delete this style?", "Are you sure you want to delete these %d styles?", n), n),
fmt_plural(n, "Are you sure you want to delete this style?", "Are you sure you want to delete these %d styles?", n),
title, wxYES_NO | wxICON_EXCLAMATION, parent);
}
@ -472,7 +473,7 @@ void DialogStyleManager::OnCatalogNew() {
// Warn about bad characters
if (badchars_removed) {
wxMessageBox(
wxString::Format(_("The specified catalog name contains one or more illegal characters. They have been replaced with underscores instead.\nThe catalog has been renamed to \"%s\"."), name),
fmt_tl("The specified catalog name contains one or more illegal characters. They have been replaced with underscores instead.\nThe catalog has been renamed to \"%s\".", name),
_("Invalid characters"));
}
@ -486,7 +487,7 @@ void DialogStyleManager::OnCatalogDelete() {
if (CatalogList->GetCount() == 1) return;
wxString name = CatalogList->GetStringSelection();
wxString message = wxString::Format(_("Are you sure you want to delete the storage \"%s\" from the catalog?"), name);
wxString message = fmt_tl("Are you sure you want to delete the storage \"%s\" from the catalog?", name);
int option = wxMessageBox(message, _("Confirm delete"), wxYES_NO | wxICON_EXCLAMATION , this);
if (option == wxYES) {
agi::fs::Remove(config::path->Decode("?user/catalog/" + from_wx(name) + ".sty"));
@ -505,7 +506,7 @@ void DialogStyleManager::OnCopyToStorage() {
wxString styleName = CurrentList->GetString(selections[i]);
if (AssStyle *style = Store.GetStyle(from_wx(styleName))) {
if (wxYES == wxMessageBox(wxString::Format(_("There is already a style with the name \"%s\" in the current storage. Overwrite?"),styleName), _("Style name collision"), wxYES_NO)) {
if (wxYES == wxMessageBox(fmt_tl("There is already a style with the name \"%s\" in the current storage. Overwrite?", styleName), _("Style name collision"), wxYES_NO)) {
*style = *styleMap.at(selections[i]);
copied.push_back(styleName);
}
@ -532,7 +533,7 @@ void DialogStyleManager::OnCopyToCurrent() {
wxString styleName = StorageList->GetString(selections[i]);
if (AssStyle *style = c->ass->GetStyle(from_wx(styleName))) {
if (wxYES == wxMessageBox(wxString::Format(_("There is already a style with the name \"%s\" in the current script. Overwrite?"), styleName), _("Style name collision"), wxYES_NO)) {
if (wxYES == wxMessageBox(fmt_tl("There is already a style with the name \"%s\" in the current script. Overwrite?", styleName), _("Style name collision"), wxYES_NO)) {
*style = *Store[selections[i]];
copied.push_back(styleName);
}
@ -709,7 +710,7 @@ void DialogStyleManager::OnCurrentImport() {
// Check if there is already a style with that name
if (AssStyle *existing = c->ass->GetStyle(styles[sel])) {
int answer = wxMessageBox(
wxString::Format(_("There is already a style with the name \"%s\" in the current script. Overwrite?"), styles[sel]),
fmt_tl("There is already a style with the name \"%s\" in the current script. Overwrite?", styles[sel]),
_("Style name collision"),
wxYES_NO);
if (answer == wxYES) {

View file

@ -32,6 +32,7 @@
#include "ass_time.h"
#include "async_video_provider.h"
#include "compat.h"
#include "format.h"
#include "help_button.h"
#include "include/aegisub/context.h"
#include "libresrc/libresrc.h"
@ -345,7 +346,7 @@ std::vector<AssDialogue*> DialogTimingProcessor::SortDialogues() {
for (auto diag : sorted) {
if (diag->Start > diag->End) {
wxMessageBox(
wxString::Format(_("One of the lines in the file (%i) has negative duration. Aborting."), diag->Row),
fmt_tl("One of the lines in the file (%i) has negative duration. Aborting.", diag->Row),
_("Invalid script"),
wxOK | wxICON_ERROR | wxCENTER);
sorted.clear();

View file

@ -28,6 +28,7 @@
#include "ass_file.h"
#include "command/command.h"
#include "compat.h"
#include "format.h"
#include "help_button.h"
#include "libresrc/libresrc.h"
#include "persist_location.h"
@ -181,7 +182,7 @@ void DialogTranslation::OnActiveLineChanged(AssDialogue *new_line) {
void DialogTranslation::OnExternalCommit(int commit_type) {
if (commit_type == AssFile::COMMIT_NEW || commit_type & AssFile::COMMIT_DIAG_ADDREM) {
line_count = c->ass->Events.size();
line_number_display->SetLabel(wxString::Format(_("Current line: %d/%d"), active_line->Row + 1, (int)line_count));
line_number_display->SetLabel(fmt_tl("Current line: %d/%d", active_line->Row + 1, line_count));
}
if (commit_type & AssFile::COMMIT_DIAG_TEXT)
@ -231,7 +232,7 @@ bool DialogTranslation::PrevBlock() {
}
void DialogTranslation::UpdateDisplay() {
line_number_display->SetLabel(wxString::Format(_("Current line: %d/%d"), active_line->Row, (int)line_count));
line_number_display->SetLabel(fmt_tl("Current line: %d/%d", active_line->Row, line_count));
original_text->SetReadOnly(false);
original_text->ClearAll();

View file

@ -34,13 +34,13 @@
#endif
#include "compat.h"
#include "format.h"
#include "options.h"
#include "string_codec.h"
#include "version.h"
#include <libaegisub/dispatch.h>
#include <libaegisub/exception.h>
#include <libaegisub/format.h>
#include <libaegisub/line_iterator.h>
#include <libaegisub/scoped_ptr.h>
@ -255,7 +255,7 @@ static wxString GetSystemLanguage() {
wxString res = GetUILanguage();
if (!res)
// On an old version of Windows, let's just return the LANGID as a string
res = wxString::Format("x-win%04x", GetUserDefaultUILanguage());
res = fmt_wx("x-win%04x", GetUserDefaultUILanguage());
return res;
}
@ -287,7 +287,7 @@ void DoCheck(bool interactive) {
if (!stream)
throw VersionCheckError(from_wx(_("Could not connect to updates server.")));
stream << agi::format(
agi::format(stream,
"GET %s?rev=%d&rel=%d&os=%s&lang=%s&aegilang=%s HTTP/1.0\r\n"
"User-Agent: Aegisub %s\r\n"
"Host: %s\r\n"
@ -309,7 +309,7 @@ void DoCheck(bool interactive) {
if (!stream || http_version.substr(0, 5) != "HTTP/")
throw VersionCheckError(from_wx(_("Could not download from updates server.")));
if (status_code != 200)
throw VersionCheckError(from_wx(wxString::Format(_("HTTP request failed, got HTTP response %d."), status_code)));
throw VersionCheckError(agi::format(_("HTTP request failed, got HTTP response %d."), status_code));
stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
@ -371,8 +371,8 @@ void PerformVersionCheck(bool interactive) {
DoCheck(interactive);
}
catch (const agi::Exception &e) {
PostErrorEvent(interactive, wxString::Format(
_("There was an error checking for updates to Aegisub:\n%s\n\nIf other applications can access the Internet fine, this is probably a temporary server problem on our end."),
PostErrorEvent(interactive, fmt_tl(
"There was an error checking for updates to Aegisub:\n%s\n\nIf other applications can access the Internet fine, this is probably a temporary server problem on our end.",
e.GetMessage()));
}
catch (...) {

View file

@ -30,6 +30,7 @@
#include "ass_time.h"
#include "async_video_provider.h"
#include "compat.h"
#include "format.h"
#include "include/aegisub/context.h"
#include "project.h"
@ -62,10 +63,10 @@ DialogVideoDetails::DialogVideoDetails(agi::Context *c)
fg->Add(new wxTextCtrl(this, -1, value, wxDefaultPosition, wxSize(300,-1), wxTE_READONLY), 0, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
};
make_field(_("File name:"), c->project->VideoName().wstring());
make_field(_("FPS:"), wxString::Format("%.3f", fps.FPS()));
make_field(_("Resolution:"), wxString::Format("%dx%d (%d:%d)", width, height, ar.numerator(), ar.denominator()));
make_field(_("Length:"), wxString::Format(wxPLURAL("1 frame", "%d frames (%s)", framecount),
framecount, to_wx(AssTime(fps.TimeAtFrame(framecount - 1)).GetAssFormated(true))));
make_field(_("FPS:"), fmt_wx("%.3f", fps.FPS()));
make_field(_("Resolution:"), fmt_wx("%dx%d (%d:%d)", width, height, ar.numerator(), ar.denominator()));
make_field(_("Length:"), fmt_plural(framecount, "1 frame", "%d frames (%s)",
framecount, AssTime(fps.TimeAtFrame(framecount - 1)).GetAssFormated(true)));
make_field(_("Decoder:"), to_wx(provider->GetDecoderName()));
wxStaticBoxSizer *video_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Video"));