diff --git a/aegisub/build/Aegisub/Aegisub.vcxproj b/aegisub/build/Aegisub/Aegisub.vcxproj index 3c80048e3..6f2a3f842 100644 --- a/aegisub/build/Aegisub/Aegisub.vcxproj +++ b/aegisub/build/Aegisub/Aegisub.vcxproj @@ -134,7 +134,6 @@ - @@ -321,7 +320,6 @@ - diff --git a/aegisub/build/Aegisub/Aegisub.vcxproj.filters b/aegisub/build/Aegisub/Aegisub.vcxproj.filters index 4beabf456..da5ea5304 100644 --- a/aegisub/build/Aegisub/Aegisub.vcxproj.filters +++ b/aegisub/build/Aegisub/Aegisub.vcxproj.filters @@ -525,9 +525,6 @@ Features\Import - - Features\Import - Features\Help @@ -1076,9 +1073,6 @@ Main UI\Grid - - Features\Import - Features\Import diff --git a/aegisub/build/libaegisub/libaegisub.vcxproj b/aegisub/build/libaegisub/libaegisub.vcxproj index 837691411..0f4655767 100644 --- a/aegisub/build/libaegisub/libaegisub.vcxproj +++ b/aegisub/build/libaegisub/libaegisub.vcxproj @@ -17,20 +17,27 @@ - $(SrcDir);$(SrcDir)include;$(AegisubContribBase)iconv\include;%(AdditionalIncludeDirectories) - NOMINMAX;%(PreprocessorDefinitions) + + $(SrcDir); + $(SrcDir)include; + $(AegisubContribBase)iconv\include; + %(AdditionalIncludeDirectories) + + + NOMINMAX; + _WIN32_WINNT=0x0501; + %(PreprocessorDefinitions) + Use lagi_pre.h lagi_pre.h - - @@ -64,8 +71,13 @@ - - + + + + + + + @@ -78,7 +90,6 @@ - @@ -96,8 +107,13 @@ - - + + + + + + + diff --git a/aegisub/build/libaegisub/libaegisub.vcxproj.filters b/aegisub/build/libaegisub/libaegisub.vcxproj.filters index 4539c7db6..75b180c34 100644 --- a/aegisub/build/libaegisub/libaegisub.vcxproj.filters +++ b/aegisub/build/libaegisub/libaegisub.vcxproj.filters @@ -26,9 +26,6 @@ Header Files - - Header Files - Header Files @@ -134,10 +131,25 @@ Header Files - + Header Files - + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + Header Files @@ -160,9 +172,6 @@ Source Files\Windows - - Source Files\Common - Source Files\Windows @@ -220,10 +229,25 @@ Source Files\Common - + Source Files\Common - + + Source Files\Common + + + Source Files\Common + + + Source Files\Windows + + + Source Files\Windows + + + Source Files\Common + + Source Files\Common @@ -232,4 +256,4 @@ Header Files - \ No newline at end of file + diff --git a/aegisub/libaegisub/Makefile b/aegisub/libaegisub/Makefile index 20d6e8ae9..040a439f8 100644 --- a/aegisub/libaegisub/Makefile +++ b/aegisub/libaegisub/Makefile @@ -25,23 +25,27 @@ SRC += \ common/charset.cpp \ common/charset_6937.cpp \ common/charset_conv.cpp \ - common/charset_ucd.cpp \ common/color.cpp \ + common/dispatch.cpp \ + common/fs.cpp \ common/hotkey.cpp \ common/io.cpp \ common/json.cpp \ common/keyframe.cpp \ + common/log.cpp \ common/mru.cpp \ common/option.cpp \ common/option_visit.cpp \ common/parser.cpp \ - common/util.cpp \ - common/log.cpp \ + common/path.cpp \ common/thesaurus.cpp \ + common/util.cpp \ common/vfr.cpp \ - unix/util.cpp \ unix/access.cpp \ - unix/log.cpp + unix/fs.cpp \ + unix/log.cpp \ + unix/path.cpp \ + unix/util.cpp ifeq (yes, $(BUILD_DARWIN)) SRC += osx/util.mm diff --git a/aegisub/libaegisub/common/charset.cpp b/aegisub/libaegisub/common/charset.cpp index 9dc1f92be..8ad4df4b6 100644 --- a/aegisub/libaegisub/common/charset.cpp +++ b/aegisub/libaegisub/common/charset.cpp @@ -16,18 +16,118 @@ /// @brief Character set detection and manipulation utilities. /// @ingroup libaegisub -#include "charset_ucd.h" +#include "libaegisub/charset.h" -namespace agi { - namespace charset { +#include "libaegisub/io.h" -std::string Detect(const std::string &file) { - return UCDetect(file).Single(); +#include +#include + +#ifndef _WIN32 +#define _X86_ 1 +#endif + +#include "../../universalchardet/nscore.h" +#include "../../universalchardet/nsUniversalDetector.h" +#include "../../universalchardet/nsMBCSGroupProber.h" +#include "../../universalchardet/nsCharSetProber.h" + +namespace { +using namespace agi::charset; + +class UCDetect : public nsUniversalDetector { + /// List of detected character sets + CharsetListDetected list; + + void Report(const char* aCharset) {} + +public: + /// @brief Detect character set of a file using UniversalCharDetect + /// @param file File to check + UCDetect(agi::fs::path const& file) + : nsUniversalDetector(NS_FILTER_ALL) + { + { + std::unique_ptr fp(agi::io::Open(file, true)); + + // If it's over 100 MB it's either binary or big enough that we won't + // be able to do anything useful with it anyway + fp->seekg(0, std::ios::end); + if (fp->tellg() > 100 * 1024 * 1024) { + list.emplace_back(1.f, "binary"); + return; + } + fp->seekg(0, std::ios::beg); + + std::streamsize binaryish = 0; + std::streamsize bytes = 0; + + while (!mDone && *fp) { + char buf[4096]; + fp->read(buf, sizeof(buf)); + std::streamsize read = fp->gcount(); + HandleData(buf, (PRUint32)read); + + // A dumb heuristic to detect binary files + if (!mDone) { + bytes += read; + for (std::streamsize i = 0; i < read; ++i) { + if ((unsigned char)buf[i] < 32 && (buf[i] != '\r' && buf[i] != '\n' && buf[i] != '\t')) + ++binaryish; + } + + if (binaryish > bytes / 8) { + list.emplace_back(1.f, "binary"); + return; + } + } + } + } + + DataEnd(); + + if (mDetectedCharset) + list.emplace_back(1.f, mDetectedCharset); + else { + switch (mInputState) { + case eHighbyte: { + for (PRInt32 i=0; iGetConfidence(); + if (conf > 0.01f) + list.emplace_back(conf, mCharSetProbers[i]->GetCharSetName()); + } + + break; + } + case ePureAscii: + list.emplace_back(1.f, "US-ASCII"); + break; + + default: + throw UnknownCharset("Unknown character set."); + } + + if (list.empty() && (mInputState == eHighbyte)) + throw UnknownCharset("Unknown character set."); + + typedef std::pair const& result; + sort(begin(list), end(list), [](result lft, result rgt) { return lft.first > rgt.first; }); + } + } + + /// @brief Detect character set of a file using UniversalCharDet + CharsetListDetected List() const { return list; } +}; } -CharsetListDetected DetectAll(const std::string& file) { - return UCDetect(file).List(); -} +namespace agi { namespace charset { + std::string Detect(agi::fs::path const& file) { + return DetectAll(file).front().second; + } - } // namespace util -} // namespace agi + CharsetListDetected DetectAll(agi::fs::path const& file) { + return UCDetect(file).List(); + } +} } diff --git a/aegisub/libaegisub/common/charset_ucd.cpp b/aegisub/libaegisub/common/charset_ucd.cpp deleted file mode 100644 index cc96f1151..000000000 --- a/aegisub/libaegisub/common/charset_ucd.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2010, Amar Takhar -// -// 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. - -/// @file charset_ucd.cpp -/// @brief Character set detection using Universalchardet -/// @ingroup libaegisub - -#include "charset_ucd.h" - -#include "libaegisub/io.h" -#include "libaegisub/scoped_ptr.h" - -#include "../../universalchardet/nsCharSetProber.h" - -namespace agi { - namespace charset { - -UCDetect::UCDetect(const std::string &file) -: nsUniversalDetector(NS_FILTER_ALL) -{ - { - agi::scoped_ptr fp(io::Open(file, true)); - - // If it's over 100 MB it's either binary or big enough that we won't - // be able to do anything useful with it anyway - fp->seekg(0, std::ios::end); - if (fp->tellg() > 100 * 1024 * 1024) { - list.emplace_back(1.f, "binary"); - return; - } - fp->seekg(0, std::ios::beg); - - std::streamsize binaryish = 0; - std::streamsize bytes = 0; - - while (!mDone && *fp) { - char buf[4096]; - fp->read(buf, sizeof(buf)); - std::streamsize read = fp->gcount(); - HandleData(buf, (PRUint32)read); - - // A dumb heuristic to detect binary files - if (!mDone) { - bytes += read; - for (std::streamsize i = 0; i < read; ++i) { - if ((unsigned char)buf[i] < 32 && (buf[i] != '\r' && buf[i] != '\n' && buf[i] != '\t')) - ++binaryish; - } - - if (binaryish > bytes / 8) { - list.emplace_back(1.f, "binary"); - return; - } - } - } - } - - DataEnd(); - - if (mDetectedCharset) - list.emplace_back(1.f, mDetectedCharset); - else { - switch (mInputState) { - case eHighbyte: { - for (PRInt32 i=0; iGetConfidence(); - if (conf > 0.01f) - list.emplace_back(conf, mCharSetProbers[i]->GetCharSetName()); - } - - break; - } - case ePureAscii: - list.emplace_back(1.f, "US-ASCII"); - break; - - default: - throw UnknownCharset("Unknown character set."); - } - - if (list.empty() && (mInputState == eHighbyte)) - throw UnknownCharset("Unknown character set."); - - typedef std::pair const& result; - sort(begin(list), end(list), [](result lft, result rgt) { return lft.first > rgt.first; }); - } -} - -std::string UCDetect::Single() const { - return list.front().second; -} - - } // namespace util -} // namespace agi diff --git a/aegisub/libaegisub/common/charset_ucd.h b/aegisub/libaegisub/common/charset_ucd.h deleted file mode 100644 index 8b144cfc0..000000000 --- a/aegisub/libaegisub/common/charset_ucd.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2010, Amar Takhar -// -// 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. - -/// @file charset_ucd.h -/// @brief Character set detection using Universalchardet -/// @ingroup libaegisub - -#include "libaegisub/charset.h" - -#include - -#ifndef _WIN32 -#define _X86_ 1 -#endif - -#include "../../universalchardet/nscore.h" -#include "../../universalchardet/nsUniversalDetector.h" -#include "../../universalchardet/nsMBCSGroupProber.h" - -namespace agi { - namespace charset { - -class UCDetect : public nsUniversalDetector { - /// List of detected character sets. - CharsetListDetected list; - - /// Stub. - void Report(const char* aCharset) {}; - -public: - - /// @brief Detect character set of a file using UniversalCharDetect - /// @param file File to check - UCDetect(const std::string &file); - - /// @brief Detect character set of a file using UniversalCharDet - CharsetListDetected List() const { return list; } - - /// @brief Return a single character set (highest confidence) - /// @return Character set - std::string Single() const; -}; - - } // namespace util -} // namespace agi diff --git a/aegisub/libaegisub/common/dispatch.cpp b/aegisub/libaegisub/common/dispatch.cpp new file mode 100644 index 000000000..fb04f6d49 --- /dev/null +++ b/aegisub/libaegisub/common/dispatch.cpp @@ -0,0 +1,98 @@ +// Copyright (c) 2013, Thomas Goyne +// +// 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 "config.h" + +#include "libaegisub/dispatch.h" + +#include +#include +#include +#include +#include + +namespace { + boost::asio::io_service *service; + std::function invoke_main; + + class MainQueue : public agi::dispatch::Queue { + void DoInvoke(agi::dispatch::Thunk thunk) { + invoke_main(thunk); + } + }; + + class BackgroundQueue : public agi::dispatch::Queue { + void DoInvoke(agi::dispatch::Thunk thunk) { + service->post(thunk); + } + }; + + class SerialQueue : public agi::dispatch::Queue { + boost::asio::io_service::strand strand; + + void DoInvoke(agi::dispatch::Thunk thunk) { + strand.post(thunk); + } + public: + SerialQueue() : strand(*service) { } + }; +} + +namespace agi { namespace dispatch { + +void Init(std::function invoke_main) { + static boost::asio::io_service service; + static boost::asio::io_service::work work(service); + ::service = &service; + ::invoke_main = invoke_main; + + for (unsigned i = 0; i < std::max(1, std::thread::hardware_concurrency()); ++i) + std::thread([]{ ::service->run(); }).detach(); +} + +void Queue::Async(Thunk thunk) { + DoInvoke(thunk); +} + +void Queue::Sync(Thunk thunk) { + std::mutex m; + std::condition_variable cv; + std::unique_lock l(m); + bool done = false; + DoInvoke([&]{ + std::unique_lock l(m); + thunk(); + done = true; + cv.notify_all(); + }); + cv.wait(l, [&]{ return done; }); +} + +Queue& Main() { + static MainQueue q; + return q; +} + +Queue& Background() { + static BackgroundQueue q; + return q; +} + +std::unique_ptr Create() { + return std::unique_ptr(new SerialQueue); +} + +} } diff --git a/aegisub/libaegisub/common/fs.cpp b/aegisub/libaegisub/common/fs.cpp new file mode 100644 index 000000000..ecdd30c6e --- /dev/null +++ b/aegisub/libaegisub/common/fs.cpp @@ -0,0 +1,106 @@ +// Copyright (c) 2013, Thomas Goyne +// +// 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 "config.h" + +#include "libaegisub/fs.h" + +#include "libaegisub/access.h" +#include "libaegisub/exception.h" +#include "libaegisub/log.h" + +#include +#include + +namespace bfs = boost::filesystem; +namespace ec = boost::system::errc; + +// boost::filesystem functions throw a single exception type for all +// errors, which isn't really what we want, so do some crazy wrapper +// shit to map error codes to more useful exceptions. +#define CHECKED_CALL(exp, src_path, dst_path) \ + boost::system::error_code ec; \ + exp; \ + switch (ec.value()) {\ + case ec::success: break; \ + case ec::no_such_file_or_directory: throw FileNotFound(src_path); \ + case ec::is_a_directory: throw NotAFile(src_path); \ + case ec::not_a_directory: throw NotADirectory(src_path); \ + case ec::no_space_on_device: throw DriveFull(dst_path); \ + case ec::permission_denied: \ + if (!src_path.empty()) \ + acs::CheckFileRead(src_path); \ + if (!dst_path.empty()) \ + acs::CheckFileWrite(dst_path); \ + throw AccessDenied(src_path); \ + default: \ + LOG_D("filesystem") << "Unknown error when calling '" << #exp << "': " << ec << ": " << ec.message(); \ + throw FileSystemUnknownError(ec.message()); \ + } + +#define CHECKED_CALL_RETURN(exp, src_path) \ + CHECKED_CALL(auto ret = exp, src_path, agi::fs::path()); \ + return ret + +#define WRAP_BFS(bfs_name, agi_name) \ + auto agi_name(path const& p) -> decltype(bfs::bfs_name(p)) { \ + CHECKED_CALL_RETURN(bfs::bfs_name(p, ec), p); \ + } + +#define WRAP_BFS_IGNORE_ERROR(bfs_name, agi_name) \ + auto agi_name(path const& p) -> decltype(bfs::bfs_name(p)) { \ + boost::system::error_code ec; \ + return bfs::bfs_name(p, ec); \ + } + +// sasuga windows.h +#undef CreateDirectory + +namespace agi { namespace fs { + WRAP_BFS_IGNORE_ERROR(exists, Exists) + WRAP_BFS_IGNORE_ERROR(is_regular_file, FileExists) + WRAP_BFS_IGNORE_ERROR(is_directory, DirectoryExists) + WRAP_BFS(file_size, SizeImpl) + WRAP_BFS(last_write_time, ModifiedTime) + WRAP_BFS(create_directories, CreateDirectory) + WRAP_BFS(space, Space) + WRAP_BFS(remove, Remove) + WRAP_BFS(canonical, Canonicalize) + + uintmax_t Size(path const& p) { + if (DirectoryExists(p)) + throw NotAFile(p); + return SizeImpl(p); + } + + uintmax_t FreeSpace(path const& p) { + return Space(p).available; + } + + void Rename(const path& from, const path& to) { + CHECKED_CALL(bfs::rename(from, to, ec), from, to); + } + + void Copy(path const& from, path const& to) { + CreateDirectory(to.parent_path()); + CHECKED_CALL(bfs::copy_file(from, to, bfs::copy_option::overwrite_if_exists, ec), from, to); + } + + bool HasExtension(path const& p, std::string const& ext) { + if (!p.has_extension()) return ext.empty(); + return boost::iequals(p.extension().string().substr(1), ext); + } +} } diff --git a/aegisub/libaegisub/common/hotkey.cpp b/aegisub/libaegisub/common/hotkey.cpp index efde67227..b42153ccf 100644 --- a/aegisub/libaegisub/common/hotkey.cpp +++ b/aegisub/libaegisub/common/hotkey.cpp @@ -18,11 +18,6 @@ #include "../config.h" -#include -#include -#include -#include - #include "libaegisub/hotkey.h" #include "libaegisub/cajun/writer.h" @@ -31,8 +26,12 @@ #include "libaegisub/json.h" #include "libaegisub/log.h" +#include #include #include +#include +#include +#include namespace agi { namespace hotkey { @@ -50,12 +49,12 @@ void Hotkey::ComboInsert(Combo const& combo) { cmd_map.insert(make_pair(combo.CmdName(), combo)); } -Hotkey::Hotkey(const std::string &file, const std::string &default_config) +Hotkey::Hotkey(agi::fs::path const& file, const std::string &default_config) : config_file(file) { LOG_D("hotkey/init") << "Generating hotkeys."; - json::Object object(agi::json_util::file(config_file, default_config)); + const json::Object object(agi::json_util::file(config_file, default_config)); for (auto const& hotkey_context : object) BuildHotkey(hotkey_context.first, hotkey_context.second); } @@ -118,7 +117,7 @@ std::vector Hotkey::GetHotkeys(const std::string &context, const st for (std::tie(it, end) = cmd_map.equal_range(command); it != end; ++it) { std::string ctext = it->second.Context(); if (ctext == "Always" || ctext == "Default" || ctext == context) - ret.push_back(it->second.StrMenu()); + ret.emplace_back(it->second.StrMenu()); } sort(ret.begin(), ret.end()); diff --git a/aegisub/libaegisub/common/io.cpp b/aegisub/libaegisub/common/io.cpp index ebd7bfe46..798b65923 100644 --- a/aegisub/libaegisub/common/io.cpp +++ b/aegisub/libaegisub/common/io.cpp @@ -16,49 +16,25 @@ /// @brief Windows IO methods. /// @ingroup libaegisub -#include -#include - -#include -#include +#include "libaegisub/io.h" #include -#include -#include "libaegisub/io.h" +#include "libaegisub/fs.h" #include "libaegisub/log.h" +#include "libaegisub/path.h" #include "libaegisub/util.h" -#ifdef _WIN32 -#define snprintf sprintf_s -#endif - -namespace { - std::string make_temp_name(std::string const& filename) { - char tmp[1024]; - snprintf(tmp, sizeof tmp, "_tmp_%lld", (long long)time(0)); - - std::string::size_type pos = filename.rfind('.'); - if (pos == std::string::npos) - return filename + tmp; - - return filename.substr(0, pos) + tmp + filename.substr(pos); - } -} +#include +#include namespace agi { namespace io { -using agi::charset::ConvertW; - -#ifndef _WIN32 -#define ConvertW -#endif - -std::ifstream* Open(const std::string &file, bool binary) { +std::ifstream* Open(fs::path const& file, bool binary) { LOG_D("agi/io/open/file") << file; acs::CheckFileRead(file); - std::ifstream *stream = new std::ifstream(ConvertW(file).c_str(), (binary ? std::ios::binary : std::ios::in)); + auto stream = new boost::filesystem::ifstream(file, (binary ? std::ios::binary : std::ios::in)); if (stream->fail()) { delete stream; @@ -68,34 +44,30 @@ std::ifstream* Open(const std::string &file, bool binary) { return stream; } -Save::Save(const std::string& file, bool binary) +Save::Save(fs::path const& file, bool binary) : file_name(file) -, tmp_name(make_temp_name(file)) +, tmp_name(unique_path(file.parent_path()/(file.stem().string() + "_tmp_%%%%." + file.extension().string()))) { LOG_D("agi/io/save/file") << file; - const std::string pwd = util::DirName(file); - - acs::CheckDirWrite(pwd); + acs::CheckDirWrite(file.parent_path()); try { acs::CheckFileWrite(file); - } catch (FileNotFoundError const&) { - // If the file doesn't exist we create a 0 byte file, this so so - // util::Rename will find it, and to let users know something went - // wrong by leaving a 0 byte file. - std::ofstream(ConvertW(file).c_str()); + } + catch (fs::FileNotFound const&) { + // Not an error } - fp = new std::ofstream(ConvertW(tmp_name).c_str(), binary ? std::ios::binary : std::ios::out); + fp = new boost::filesystem::ofstream(tmp_name, binary ? std::ios::binary : std::ios::out); if (!fp->good()) { delete fp; - throw agi::FileNotAccessibleError("Could not create temporary file at: " + tmp_name); + throw fs::WriteDenied(tmp_name); } } Save::~Save() { - delete fp; - util::Rename(tmp_name, file_name); + delete fp; // Explicitly delete to unlock file on Windows + fs::Rename(tmp_name, file_name); } std::ofstream& Save::Get() { diff --git a/aegisub/libaegisub/common/json.cpp b/aegisub/libaegisub/common/json.cpp index 8f9f725f8..dfa40e7d5 100644 --- a/aegisub/libaegisub/common/json.cpp +++ b/aegisub/libaegisub/common/json.cpp @@ -18,21 +18,22 @@ #include "../config.h" +#include "libaegisub/json.h" + #include +#include #include +#include "libaegisub/fs.h" #include "libaegisub/io.h" -#include "libaegisub/json.h" #include "libaegisub/log.h" -#include "libaegisub/scoped_ptr.h" - namespace agi { namespace json_util { json::UnknownElement parse(std::istream *stream) { try { - agi::scoped_ptr stream_deleter(stream); + std::unique_ptr stream_deleter(stream); json::UnknownElement root; json::Reader::Read(root, *stream); @@ -46,15 +47,15 @@ json::UnknownElement parse(std::istream *stream) { } } -json::UnknownElement file(const std::string &file) { +json::UnknownElement file(agi::fs::path const& file) { return parse(io::Open(file)); } -json::UnknownElement file(const std::string &file, const std::string &default_config) { +json::UnknownElement file(agi::fs::path const& file, const std::string &default_config) { try { return parse(io::Open(file)); } - catch (FileNotFoundError const&) { + catch (fs::FileNotFound const&) { // Not an error return parse(new std::istringstream(default_config)); } diff --git a/aegisub/libaegisub/common/keyframe.cpp b/aegisub/libaegisub/common/keyframe.cpp index 3dbcda834..fa98c41ca 100644 --- a/aegisub/libaegisub/common/keyframe.cpp +++ b/aegisub/libaegisub/common/keyframe.cpp @@ -17,16 +17,15 @@ /// @ingroup libaegisub /// - #include "../config.h" +#include "libaegisub/keyframe.h" + #include #include #include "libaegisub/io.h" #include "libaegisub/line_iterator.h" -#include "libaegisub/keyframe.h" -#include "libaegisub/vfr.h" #include #include @@ -77,7 +76,7 @@ char x264(std::string const& line) { } namespace agi { namespace keyframe { -void Save(std::string const& filename, std::vector const& keyframes) { +void Save(agi::fs::path const& filename, std::vector const& keyframes) { io::Save file(filename); std::ofstream& of = file.Get(); of << "# keyframe format v1" << std::endl; @@ -85,7 +84,7 @@ void Save(std::string const& filename, std::vector const& keyframes) { boost::copy(keyframes, std::ostream_iterator(of, "\n")); } -std::vector Load(std::string const& filename) { +std::vector Load(agi::fs::path const& filename) { std::unique_ptr file(io::Open(filename)); std::istream &is(*file); diff --git a/aegisub/libaegisub/common/log.cpp b/aegisub/libaegisub/common/log.cpp index 016870ed2..8486a575e 100644 --- a/aegisub/libaegisub/common/log.cpp +++ b/aegisub/libaegisub/common/log.cpp @@ -18,21 +18,20 @@ #include "../config.h" -#include -#include -#include -#include -#include -#include +#include "libaegisub/log.h" #include "libaegisub/cajun/elements.h" #include "libaegisub/cajun/writer.h" #include "libaegisub/io.h" -#include "libaegisub/log.h" #include "libaegisub/types.h" #include "libaegisub/util.h" +#include #include +#include +#include +#include +#include namespace agi { namespace log { @@ -109,7 +108,7 @@ Message::~Message() { agi::log::log->log(sm); } -JsonEmitter::JsonEmitter(std::string const& directory, const agi::log::LogSink *log_sink) +JsonEmitter::JsonEmitter(agi::fs::path const& directory, const agi::log::LogSink *log_sink) : directory(directory) , log_sink(log_sink) { @@ -146,9 +145,7 @@ JsonEmitter::~JsonEmitter() { timeval_close.push_back((int64_t)time_close.tv_sec); timeval_close.push_back((int64_t)time_close.tv_usec); - std::stringstream str; - str << directory << time_start.tv_sec << ".json"; - json::Writer::Write(root, io::Save(str.str()).Get()); + json::Writer::Write(root, io::Save(directory/(std::to_string(time_start.tv_sec) + ".json")).Get()); } } // namespace log diff --git a/aegisub/libaegisub/common/mru.cpp b/aegisub/libaegisub/common/mru.cpp index 8f5729c27..6ca4b8b95 100644 --- a/aegisub/libaegisub/common/mru.cpp +++ b/aegisub/libaegisub/common/mru.cpp @@ -30,7 +30,7 @@ namespace agi { -MRUManager::MRUManager(std::string const& config, std::string const& default_config, agi::Options *options) +MRUManager::MRUManager(agi::fs::path const& config, std::string const& default_config, agi::Options *options) : config_name(config) , options(options) { @@ -60,16 +60,22 @@ MRUManager::MRUListMap &MRUManager::Find(std::string const& key) { return index->second; } -void MRUManager::Add(std::string const& key, std::string const& entry) { +void MRUManager::Add(std::string const& key, agi::fs::path const& entry) { MRUListMap &map = Find(key); - map.remove(entry); - map.push_front(entry); - Prune(key, map); + auto it = find(begin(map), end(map), entry); + if (it == begin(map) && it != end(map)) + return; + if (it != end(map)) + map.splice(begin(map), map, it); + else { + map.push_front(entry); + Prune(key, map); + } Flush(); } -void MRUManager::Remove(std::string const& key, std::string const& entry) { +void MRUManager::Remove(std::string const& key, agi::fs::path const& entry) { Find(key).remove(entry); Flush(); @@ -79,7 +85,7 @@ const MRUManager::MRUListMap* MRUManager::Get(std::string const& key) { return &Find(key); } -std::string const& MRUManager::GetEntry(std::string const& key, const size_t entry) { +agi::fs::path const& MRUManager::GetEntry(std::string const& key, const size_t entry) { const MRUManager::MRUListMap *const map = Get(key); if (entry >= map->size()) @@ -93,7 +99,8 @@ void MRUManager::Flush() { for (auto const& mru_map : mru) { json::Array &array = out[mru_map.first]; - array.insert(array.end(), mru_map.second.begin(), mru_map.second.end()); + transform(begin(mru_map.second), end(mru_map.second), + back_inserter(array), [](agi::fs::path const& p) { return p.string(); }); } json::Writer::Write(out, io::Save(config_name).Get()); @@ -116,7 +123,8 @@ void MRUManager::Prune(std::string const& key, MRUListMap& map) const { /// @param array json::Array of values. void MRUManager::Load(std::string const& key, const json::Array& array) { try { - copy(array.begin(), array.end(), back_inserter(mru[key])); + transform(begin(array), end(array), + back_inserter(mru[key]), [](std::string const& s) { return s; }); } catch (json::Exception const&) { // Out of date MRU file; just discard the data and skip it diff --git a/aegisub/libaegisub/common/option.cpp b/aegisub/libaegisub/common/option.cpp index 14147c11e..f57bd6a91 100644 --- a/aegisub/libaegisub/common/option.cpp +++ b/aegisub/libaegisub/common/option.cpp @@ -20,14 +20,11 @@ #include "libaegisub/option.h" -#include -#include -#include - #include "libaegisub/cajun/reader.h" #include "libaegisub/cajun/writer.h" #include "libaegisub/cajun/elements.h" +#include "libaegisub/fs.h" #include "libaegisub/io.h" #include "libaegisub/log.h" #include "libaegisub/option_value.h" @@ -35,6 +32,9 @@ #include "option_visit.h" #include +#include +#include +#include namespace { /// @brief Write an option to a json object @@ -66,7 +66,7 @@ namespace { namespace agi { -Options::Options(const std::string &file, const std::string& default_config, const OptionSetting setting) +Options::Options(agi::fs::path const& file, const std::string& default_config, const OptionSetting setting) : config_file(file) , setting(setting) { @@ -88,21 +88,17 @@ void Options::ConfigNext(std::istream& stream) { } void Options::ConfigUser() { - std::unique_ptr stream; - try { - stream.reset(agi::io::Open(config_file)); - } catch (const FileNotFoundError&) { + std::unique_ptr stream(io::Open(config_file)); + LoadConfig(*stream, true); + } + catch (fs::FileNotFound const&) { return; } - /// @todo Handle other errors such as parsing and notifying the user. - LoadConfig(*stream, true); } void Options::LoadConfig(std::istream& stream, bool ignore_errors) { - /// @todo Store all previously loaded configs in an array for bug report purposes, - /// this is just a temp stub. json::UnknownElement config_root; try { diff --git a/aegisub/libaegisub/common/path.cpp b/aegisub/libaegisub/common/path.cpp new file mode 100644 index 000000000..680495b8a --- /dev/null +++ b/aegisub/libaegisub/common/path.cpp @@ -0,0 +1,150 @@ +// Copyright (c) 2013, Thomas Goyne +// +// 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/ + +/// @file path.cpp +/// @brief Platform-independent path code +/// @ingroup libaegisub + +#include "../config.h" + +#include "libaegisub/path.h" + +#include "libaegisub/fs.h" +#include "libaegisub/util.h" + +#include +#include +#include + +namespace { + template + typename T::const_iterator last_less_than(T const& cont, U const& value) { + auto it = cont.upper_bound(value); + if (it != cont.begin()) + --it; + return it; + } +} + +namespace agi { + +Path::Path() { + tokens["?user"]; + tokens["?local"]; + tokens["?data"]; + tokens["?temp"]; + tokens["?dictionary"]; + tokens["?docs"]; + + FillPlatformSpecificPaths(); + + tokens["?audio"]; + tokens["?script"]; + tokens["?video"]; +} + +fs::path Path::Decode(std::string const& path) const { + const auto it = last_less_than(tokens, path); + + if (!it->second.empty() && boost::starts_with(path, it->first)) + return (it->second/path.substr(it->first.size())).make_preferred(); + return fs::path(path).make_preferred(); +} + +fs::path Path::MakeRelative(fs::path const& path, std::string const& token) const { + const auto it = tokens.find(token); + if (it == tokens.end()) throw agi::InternalError("Bad token: " + token, 0); + + return MakeRelative(path, it->second); +} + +fs::path Path::MakeRelative(fs::path const& path, fs::path const& base) const { + if (path.empty() || base.empty()) return path; + + const auto str = path.string(); + if (boost::starts_with(str, "?dummy") || boost::starts_with(str, "dummy-audio:")) + return path; + + // Paths on different volumes can't be made relative to each other + if (path.has_root_name() && path.root_name() != base.root_name()) + return path.string(); + + auto path_it = path.begin(); + auto ref_it = base.begin(); + for (; path_it != path.end() && ref_it != base.end() && *path_it == *ref_it; ++path_it, ++ref_it) ; + + agi::fs::path result; + for (; ref_it != base.end(); ++ref_it) + result /= ".."; + for (; path_it != path.end(); ++path_it) + result /= *path_it; + + return result; +} + +fs::path Path::MakeAbsolute(fs::path path, std::string const& token) const { + const auto it = tokens.find(token); + if (it == tokens.end()) throw agi::InternalError("Bad token: " + token, 0); + if (path.empty()) return path; + + path.make_preferred(); + const auto str = path.string(); + if (boost::starts_with(str, "?dummy") || boost::starts_with(str, "dummy-audio:")) + return path; + return (it->second.empty() || path.is_absolute()) ? path : it->second/path; +} + +std::string Path::Encode(fs::path const& path) const { + // Find the shortest encoding of path made relative to each token + std::string shortest = path.string(); + size_t length = boost::distance(path); + for (auto const& tok : tokens) { + if (tok.second.empty()) continue; + + const auto p = MakeRelative(path, tok.first); + const size_t d = boost::distance(p); + if (d < length) { + length = d; + shortest = (tok.first/p).string(); + } + } + + return shortest; +} + +void Path::SetToken(std::string const& token_name, fs::path const& token_value) { + const auto it = tokens.find(token_name); + if (it == tokens.end()) throw agi::InternalError("Bad token: " + token_name, 0); + + if (token_value.empty()) + it->second = token_value; + else if (!token_value.is_absolute()) + it->second.clear(); + else { + it->second = token_value; + it->second.make_preferred(); + if (fs::FileExists(it->second)) + it->second = it->second.parent_path(); + } + + paths.clear(); + for (auto const& tok : tokens) { + if (!tok.second.empty()) + paths[tok.second] = tok.first; + } +} + +} diff --git a/aegisub/libaegisub/common/thesaurus.cpp b/aegisub/libaegisub/common/thesaurus.cpp index 956fb144a..0c66368c5 100644 --- a/aegisub/libaegisub/common/thesaurus.cpp +++ b/aegisub/libaegisub/common/thesaurus.cpp @@ -26,16 +26,16 @@ #include #include -#include +#include using boost::phoenix::placeholders::_1; namespace agi { -Thesaurus::Thesaurus(std::string const& dat_path, std::string const& idx_path) +Thesaurus::Thesaurus(agi::fs::path const& dat_path, agi::fs::path const& idx_path) : dat(io::Open(dat_path)) { - scoped_ptr idx(io::Open(idx_path)); + std::unique_ptr idx(io::Open(idx_path)); std::string encoding_name; getline(*idx, encoding_name); diff --git a/aegisub/libaegisub/common/util.cpp b/aegisub/libaegisub/common/util.cpp index 52ddb0c12..b1b738e23 100644 --- a/aegisub/libaegisub/common/util.cpp +++ b/aegisub/libaegisub/common/util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011, Amar Takhar +// Copyright (c) 2013, Thomas Goyne // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -11,36 +11,26 @@ // 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/ -/// @file util.cpp -/// @brief Unix utility methods. -/// @ingroup libaegisub - -#include - -#include -#include +#include "../config.h" #include "libaegisub/util.h" -#include +#include -namespace agi { - namespace util { +namespace agi { namespace util { -void str_lower(std::string &str) { - boost::to_lower(str); +std::string strftime(const char *fmt, const tm *tmptr) { + if (!tmptr) { + time_t t = time(nullptr); + tmptr = localtime(&t); + } + + char buff[65536]; + ::strftime(buff, sizeof buff, fmt, tmptr); + return buff; } -int strtoi(std::string const& str) { - errno = 0; - long l = strtol(str.c_str(), nullptr, 10); - - if ((errno == ERANGE) || (l < INT_MIN) || (l > INT_MAX)) - return 0; - - return (int)l; -} - - } // namespace util -} // namespace agi +} } diff --git a/aegisub/libaegisub/common/vfr.cpp b/aegisub/libaegisub/common/vfr.cpp index 897c26850..39288741f 100644 --- a/aegisub/libaegisub/common/vfr.cpp +++ b/aegisub/libaegisub/common/vfr.cpp @@ -16,10 +16,13 @@ /// @brief Framerate handling of all sorts /// @ingroup libaegisub video_input +#include "../config.h" + #include "libaegisub/vfr.h" #include #include +#include #include #include #include @@ -199,7 +202,7 @@ Framerate &Framerate::operator=(double fps) { return *this = Framerate(fps); } -Framerate::Framerate(std::string const& filename) +Framerate::Framerate(fs::path const& filename) : denominator(default_denominator) , numerator(0) { @@ -221,7 +224,7 @@ Framerate::Framerate(std::string const& filename) throw UnknownFormat(line); } -void Framerate::Save(std::string const& filename, int length) const { +void Framerate::Save(fs::path const& filename, int length) const { agi::io::Save file(filename); std::ofstream &out = file.Get(); diff --git a/aegisub/libaegisub/include/libaegisub/access.h b/aegisub/libaegisub/include/libaegisub/access.h index ee5ecb01e..f58d2d0fa 100644 --- a/aegisub/libaegisub/include/libaegisub/access.h +++ b/aegisub/libaegisub/include/libaegisub/access.h @@ -17,17 +17,11 @@ /// @ingroup libaegisub #include +#include namespace agi { namespace acs { -DEFINE_SIMPLE_EXCEPTION_NOINNER(Fatal, FileSystemError, "filesystem/fatal"); -DEFINE_SIMPLE_EXCEPTION_NOINNER(NotAFile, FileSystemError, "filesystem/not_a_file") -DEFINE_SIMPLE_EXCEPTION_NOINNER(NotADirectory, FileSystemError, "filesystem/not_a_directory") - -DEFINE_SIMPLE_EXCEPTION_NOINNER(Read, FileNotAccessibleError, "filesystem/not_accessible/read_permission") -DEFINE_SIMPLE_EXCEPTION_NOINNER(Write, FileNotAccessibleError, "filesystem/not_accessible/write_permission") - enum Type { FileRead, DirRead, @@ -35,13 +29,13 @@ enum Type { DirWrite }; -void Check(const std::string &file, acs::Type); +void Check(fs::path const& file, acs::Type); -void CheckFileRead(const std::string &file); -void CheckDirRead(const std::string &dir); +inline void CheckFileRead(fs::path const& file) { Check(file, acs::FileRead); } +inline void CheckFileWrite(fs::path const& file) { Check(file, acs::FileWrite); } -void CheckFileWrite(const std::string &file); -void CheckDirWrite(const std::string &dir); +inline void CheckDirRead(fs::path const& dir) { Check(dir, acs::DirRead); } +inline void CheckDirWrite(fs::path const& dir) { Check(dir, acs::DirWrite); } } // namespace axs } // namespace agi diff --git a/aegisub/libaegisub/include/libaegisub/charset.h b/aegisub/libaegisub/include/libaegisub/charset.h index 971a3ee08..0c6cdbd17 100644 --- a/aegisub/libaegisub/include/libaegisub/charset.h +++ b/aegisub/libaegisub/include/libaegisub/charset.h @@ -16,11 +16,11 @@ /// @brief Character set detection and manipulation utilities. /// @ingroup libaegisub -#include -#include +#include +#include + #include #include -#include namespace agi { /// Character set conversion and detection. @@ -35,12 +35,12 @@ typedef std::vector> CharsetListDetected; /// @brief Return a complete list of detected character sets ordered by precedence. /// @param file File to check /// @return List of possible charsets sorted by probability -CharsetListDetected DetectAll(std::string const& file); +CharsetListDetected DetectAll(agi::fs::path const& file); /// @brief Returns the character set with the highest confidence /// @param file File to check /// @return Detected character set. -std::string Detect(const std::string &file); +std::string Detect(agi::fs::path const& file); } // namespace util } // namespace agi diff --git a/aegisub/libaegisub/include/libaegisub/charset_conv_win.h b/aegisub/libaegisub/include/libaegisub/charset_conv_win.h index f7551daa9..ad89cf3d8 100644 --- a/aegisub/libaegisub/include/libaegisub/charset_conv_win.h +++ b/aegisub/libaegisub/include/libaegisub/charset_conv_win.h @@ -23,5 +23,8 @@ namespace agi { /// Convert a UTF-8 string to a string suitable for use with Win32 API functions std::wstring ConvertW(std::string const& src); std::string ConvertW(std::wstring const& src); + + /// Convert a UTF-16 string to the local charset + std::string ConvertLocal(std::wstring const& src); } } diff --git a/aegisub/libaegisub/include/libaegisub/dispatch.h b/aegisub/libaegisub/include/libaegisub/dispatch.h new file mode 100644 index 000000000..d8be32fe3 --- /dev/null +++ b/aegisub/libaegisub/include/libaegisub/dispatch.h @@ -0,0 +1,50 @@ +// Copyright (c) 2013, Thomas Goyne +// +// 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 +#include + +namespace agi { + namespace dispatch { + typedef std::function Thunk; + + class Queue { + virtual void DoInvoke(Thunk thunk)=0; + public: + virtual ~Queue() { } + + /// Invoke the thunk on this processing queue, returning immediately + void Async(Thunk thunk); + + /// Invoke the thunk on this processing queue, returning only when + /// it's complete + void Sync(Thunk thunk); + }; + + /// Initialize the dispatch thread pools + /// @param invoke_main A function which invokes the thunk on the GUI thread + void Init(std::function invoke_main); + + /// Get the main queue, which runs on the GUI thread + Queue& Main(); + + /// Get the generic background queue, which runs thunks in parallel + Queue& Background(); + + /// Create a new serial queue + std::unique_ptr Create(); + } +} diff --git a/aegisub/libaegisub/include/libaegisub/exception.h b/aegisub/libaegisub/include/libaegisub/exception.h index cd4f34693..2290d80ad 100644 --- a/aegisub/libaegisub/include/libaegisub/exception.h +++ b/aegisub/libaegisub/include/libaegisub/exception.h @@ -37,10 +37,7 @@ #include #include -/// @see aegisub.h namespace agi { - - /// @class Exception /// @brief Base class for all exceptions in Aegisub. /// @@ -90,7 +87,6 @@ namespace agi { /// could not be opened for reading". This is the purpose of the "inner" /// exceptions. class Exception { - /// The error message std::string message; @@ -98,16 +94,13 @@ namespace agi { std::shared_ptr inner; protected: - /// @brief Protected constructor initialising members /// @param msg The error message /// @param inr The inner exception, optional /// /// Deriving classes should always use this constructor for initialising /// the base class. - Exception(const std::string &msg, const Exception *inr = 0) - : message(msg) - { + Exception(const std::string &msg, const Exception *inr = 0) : message(msg) { if (inr) inner.reset(inr->Copy()); } @@ -120,9 +113,7 @@ namespace agi { public: /// @brief Destructor - virtual ~Exception() - { - } + virtual ~Exception() { } /// @brief Get the outer exception error message /// @return Error message @@ -147,7 +138,6 @@ namespace agi { /// level are [a-z0-9_]. virtual const char * GetName() const = 0; - /// @brief Convert to char array as the error message /// @return The error message operator const char * () { return GetMessage().c_str(); } @@ -164,16 +154,12 @@ namespace agi { virtual Exception *Copy() const = 0; }; - - /// @brief Convenience macro to include the current location in code /// /// Intended for use in error messages where it can sometimes be convenient to /// indicate the exact position the error occurred at. #define AG_WHERE " (at " __FILE__ ":" #__LINE__ ")" - - /// @brief Convenience macro for declaring exceptions with no support for inner exception /// @param classname Name of the exception class to declare /// @param baseclass Class to derive from @@ -231,7 +217,6 @@ namespace agi { classname(const std::string &msg, Exception *inner) : baseclass(msg, inner) { } \ }; - /// @class agi::UserCancelException /// @extends agi::Exception /// @brief Exception for "user cancel" events @@ -246,7 +231,6 @@ namespace agi { /// code in question. DEFINE_SIMPLE_EXCEPTION_NOINNER(UserCancelException,Exception,"nonerror/user_cancel") - /// @class agi::InternalError /// @extends agi:Exception /// @brief Errors that should never happen and point to some invalid assumption in the code @@ -258,42 +242,17 @@ namespace agi { /// and eventually cause an abort(). DEFINE_SIMPLE_EXCEPTION(InternalError, Exception, "internal_error") - - /// @class agi::FileSystemError - /// @extends agi::Exception - /// @brief Base class for errors related to the file system + /// @class agi::EnvironmentError + /// @extends agi:Exception + /// @brief The execution environment is broken in some fundamental way /// - /// This base class can not be instantiated. - /// File system errors do not support inner exceptions, as they are always originating - /// causes for errors. - DEFINE_BASE_EXCEPTION_NOINNER(FileSystemError,Exception) - - /// @class agi::FileNotAccessibleError - /// @extends agi::FileSystemError - /// @brief A file can't be accessed for some reason - DEFINE_SIMPLE_EXCEPTION_NOINNER(FileNotAccessibleError,FileSystemError,"filesystem/not_accessible") - - - /// @class FileNotFoundError - /// @brief A file can't be accessed because there's no file by the given name - class FileNotFoundError : public FileNotAccessibleError { - public: - - /// @brief Constructor, automatically builds the error message - /// @param filename Name of the file that could not be found - FileNotFoundError(const std::string &filename) : FileNotAccessibleError(std::string("File not found: ") + filename) { } - - // Not documented, see agi::Exception class - const char * GetName() const { return "filesystem/not_accessible/not_found"; } - - // Not documented, see agi::Exception class - Exception * Copy() const { return new FileNotFoundError(*this); } - }; - + /// Throw an environment error when a call to the platform API has failed + /// in some way that should normally never happen or suggests that the + /// runtime environment is too insane to support. + DEFINE_SIMPLE_EXCEPTION_NOINNER(EnvironmentError, Exception, "environment_error") /// @class agi::InvalidInputException /// @extends agi::Exception /// @brief Some input data were invalid and could not be processed - DEFINE_BASE_EXCEPTION(InvalidInputException,Exception) - + DEFINE_BASE_EXCEPTION(InvalidInputException, Exception) } diff --git a/aegisub/libaegisub/include/libaegisub/fs.h b/aegisub/libaegisub/include/libaegisub/fs.h new file mode 100644 index 000000000..c1516e971 --- /dev/null +++ b/aegisub/libaegisub/include/libaegisub/fs.h @@ -0,0 +1,177 @@ +// Copyright (c) 2013, Thomas Goyne +// +// 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 +#include + +#include +#include +#include +#include +#include +#include + +#undef CreateDirectory + +namespace agi { + namespace fs { + /// Define a filesystem error which takes a path or a string +#define DEFINE_FS_EXCEPTION(type, base, message) \ + struct type : public base { \ + type(path const& p) : base(message + p.string()) { } \ + type(std::string const& s) : base(s) { } \ + const char *GetName() const { return ""; } \ + Exception *Copy() const { return new type(*this); } \ + } + + /// @class agi::FileSystemError + /// @extends agi::Exception + /// @brief Base class for errors related to the file system + /// + /// This base class can not be instantiated. + /// File system errors do not support inner exceptions, as they + /// are always originating causes for errors. + DEFINE_BASE_EXCEPTION_NOINNER(FileSystemError, Exception) + + /// A file can't be accessed for some reason + DEFINE_FS_EXCEPTION(FileNotAccessible, FileSystemError, "File is not accessible: "); + + /// A file can't be accessed because there's no file by the given name + DEFINE_FS_EXCEPTION(FileNotFound, FileNotAccessible, "File not found: "); + + /// An error of some unknown type has occured + DEFINE_SIMPLE_EXCEPTION_NOINNER(FileSystemUnknownError, FileSystemError, "filesystem/unknown"); + + /// The path exists, but isn't a file + DEFINE_FS_EXCEPTION(NotAFile, FileNotAccessible, "Path is not a file (and should be): "); + + /// The path exists, but isn't a directory + DEFINE_FS_EXCEPTION(NotADirectory, FileNotAccessible, "Path is not a directory (and should be): "); + + /// The given path is too long for the filesystem + DEFINE_FS_EXCEPTION(PathTooLog, FileSystemError, "Path is too long: "); + + /// Insufficient free space to complete operation + DEFINE_FS_EXCEPTION(DriveFull, FileSystemError, "Insufficient free space to write file: "); + + /// Base class for access denied errors + DEFINE_FS_EXCEPTION(AccessDenied, FileNotAccessible, "Access denied to path: "); + + /// Trying to read the file gave an access denied error + DEFINE_FS_EXCEPTION(ReadDenied, AccessDenied, "Access denied when trying to read: "); + + /// Trying to write the file gave an access denied error + DEFINE_FS_EXCEPTION(WriteDenied, AccessDenied, "Access denied when trying to write: "); + + /// File exists and cannot be overwritten due to being read-only + DEFINE_FS_EXCEPTION(ReadOnlyFile, WriteDenied, "File is read-only: "); + + bool Exists(path const& p); + bool FileExists(path const& file); + bool DirectoryExists(path const& dir); + + /// Get the local-charset encoded shortname for a file + /// + /// This is purely for compatibility with external libraries which do + /// not support unicode filenames on Windows. On all other platforms, + /// it is a no-op. + std::string ShortName(path const& file_path); + + /// Check for amount of free space on a path + uintmax_t FreeSpace(path const& dir_path); + + /// Get the size in bytes of the file at path + /// + /// @throws agi::FileNotFound if path does not exist + /// @throws agi::acs::NotAFile if path is a directory + /// @throws agi::acs::Read if path exists but could not be read + uintmax_t Size(path const& file_path); + + /// Get the modification time of the file at path + /// + /// @throws agi::FileNotFound if path does not exist + /// @throws agi::acs::NotAFile if path is a directory + /// @throws agi::acs::Read if path exists but could not be read + time_t ModifiedTime(path const& file_path); + + /// Create a directory and all required intermediate directories + /// @throws agi::acs::Write if the directory could not be created. + /// + /// Trying to create a directory which already exists is not an error. + bool CreateDirectory(path const& dir_path); + + /// Touch the given path + /// + /// Creates the file if it does not exist, or updates the modified + /// time if it does + void Touch(path const& file_path); + + /// Rename a file or directory + /// @param from Source path + /// @param to Destination path + void Rename(path const& from, path const& to); + + /// Copy a file + /// @param from Source path + /// @param to Destination path + /// + /// The destination path will be created if it does not exist. + void Copy(path const& from, path const& to); + + /// Delete a file + /// @param path Path to file to delete + /// @throws agi::FileNotAccessibleError if file exists but could not be deleted + bool Remove(path const& file); + + /// Check if the file has the given extension + /// @param p Path to check + /// @param ext Case-insensitive extension, without leading dot + bool HasExtension(path const& p, std::string const& ext); + + agi::fs::path Canonicalize(agi::fs::path const& path); + + class DirectoryIterator { + struct PrivData; + std::shared_ptr privdata; + std::string value; + public: + typedef path value_type; + typedef path* pointer; + typedef path& reference; + typedef size_t difference_type; + typedef std::forward_iterator_tag iterator_category; + + bool operator==(DirectoryIterator const&) const; + bool operator!=(DirectoryIterator const& rhs) const { return !(*this == rhs); } + DirectoryIterator& operator++(); + std::string const& operator*() const { return value; } + + DirectoryIterator(path const& p, std::string const& filter); + DirectoryIterator(); + ~DirectoryIterator(); + + template void GetAll(T& cont); + }; + + inline DirectoryIterator& begin(DirectoryIterator &it) { return it; } + inline DirectoryIterator end(DirectoryIterator &) { return DirectoryIterator(); } + + template + inline void DirectoryIterator::GetAll(T& cont) { + copy(*this, end(*this), std::back_inserter(cont)); + } + } +} diff --git a/aegisub/libaegisub/include/libaegisub/fs_fwd.h b/aegisub/libaegisub/include/libaegisub/fs_fwd.h new file mode 100644 index 000000000..c2d886964 --- /dev/null +++ b/aegisub/libaegisub/include/libaegisub/fs_fwd.h @@ -0,0 +1,18 @@ +// Copyright (c) 2013, Thomas Goyne +// +// 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/ + +namespace boost { namespace filesystem { class path; } } +namespace agi { namespace fs { typedef boost::filesystem::path path; } } diff --git a/aegisub/libaegisub/include/libaegisub/hotkey.h b/aegisub/libaegisub/include/libaegisub/hotkey.h index 6fd0dcff9..eaaa73dea 100644 --- a/aegisub/libaegisub/include/libaegisub/hotkey.h +++ b/aegisub/libaegisub/include/libaegisub/hotkey.h @@ -16,11 +16,13 @@ /// @brief Hotkey handler /// @ingroup hotkey menu event window +#include #include #include #include #include +#include #include namespace json { @@ -75,9 +77,9 @@ public: /// Map to hold Combo instances typedef std::multimap HotkeyMap; private: - HotkeyMap str_map; ///< String representation -> Combo - HotkeyMap cmd_map; ///< Command name -> Combo - const std::string config_file; ///< Default user config location. + HotkeyMap str_map; ///< String representation -> Combo + HotkeyMap cmd_map; ///< Command name -> Combo + const agi::fs::path config_file; ///< Default user config location. /// Build hotkey map. /// @param context Context being parsed. @@ -97,7 +99,7 @@ public: /// Constructor /// @param file Location of user config file. /// @param default_config Default config. - Hotkey(const std::string &file, const std::string &default_config); + Hotkey(agi::fs::path const& file, const std::string &default_config); /// Scan for a matching key. /// @param context Context requested. diff --git a/aegisub/libaegisub/include/libaegisub/io.h b/aegisub/libaegisub/include/libaegisub/io.h index ca9c5e02c..b23d73a29 100644 --- a/aegisub/libaegisub/include/libaegisub/io.h +++ b/aegisub/libaegisub/include/libaegisub/io.h @@ -16,10 +16,11 @@ /// @brief Public interface for IO methods. /// @ingroup libaegisub -#include -#include - #include +#include + +#include +#include namespace agi { namespace io { @@ -27,15 +28,15 @@ namespace agi { DEFINE_BASE_EXCEPTION_NOINNER(IOError, Exception) DEFINE_SIMPLE_EXCEPTION_NOINNER(IOFatal, IOError, "io/fatal") -std::ifstream* Open(const std::string &file, bool binary = false); +std::ifstream* Open(fs::path const& file, bool binary = false); class Save { std::ofstream *fp; - const std::string file_name; - const std::string tmp_name; + const fs::path file_name; + const fs::path tmp_name; public: - Save(const std::string& file, bool binary = false); + Save(fs::path const& file, bool binary = false); ~Save(); std::ofstream& Get(); }; diff --git a/aegisub/libaegisub/include/libaegisub/json.h b/aegisub/libaegisub/include/libaegisub/json.h index 04adc8d22..e132120fb 100644 --- a/aegisub/libaegisub/include/libaegisub/json.h +++ b/aegisub/libaegisub/include/libaegisub/json.h @@ -18,6 +18,7 @@ #include #include +#include namespace agi { namespace json_util { @@ -30,14 +31,13 @@ json::UnknownElement parse(std::istream *stream); /// Parse a JSON file. /// @param file Path JSON to file /// @return json::UnknownElement -json::UnknownElement file(const std::string &file); +json::UnknownElement file(agi::fs::path const& file); /// Parse a json stream, with default handler. /// @param file Path to JSON file. /// @param Default config file to load incase of nonexistent file /// @return json::UnknownElement -json::UnknownElement file(const std::string &file, const std::string &default_config); - +json::UnknownElement file(agi::fs::path const& file, const std::string &default_config); } // namespace json_util } // namespace agi diff --git a/aegisub/libaegisub/include/libaegisub/keyframe.h b/aegisub/libaegisub/include/libaegisub/keyframe.h index 55523e490..812b98469 100644 --- a/aegisub/libaegisub/include/libaegisub/keyframe.h +++ b/aegisub/libaegisub/include/libaegisub/keyframe.h @@ -20,6 +20,7 @@ #include #include "exception.h" +#include "fs_fwd.h" namespace agi { namespace vfr { class Framerate; } @@ -27,11 +28,12 @@ namespace agi { /// @brief Load a keyframe file /// @param filename File to load /// @return List of frame numbers which are keyframes - std::vector Load(std::string const& filename); + std::vector Load(agi::fs::path const& filename); + /// @brief Save keyframes to a file /// @param filename File to save to /// @param keyframes List of keyframes to save - void Save(std::string const& filename, std::vector const& keyframes); + void Save(agi::fs::path const& filename, std::vector const& keyframes); DEFINE_SIMPLE_EXCEPTION_NOINNER(Error, Exception, "keyframe/error") } diff --git a/aegisub/libaegisub/include/libaegisub/line_iterator.h b/aegisub/libaegisub/include/libaegisub/line_iterator.h index f8c96aa5a..f149df6a6 100644 --- a/aegisub/libaegisub/include/libaegisub/line_iterator.h +++ b/aegisub/libaegisub/include/libaegisub/line_iterator.h @@ -146,6 +146,13 @@ public: } }; +// Enable range-based for +template +line_iterator& begin(line_iterator& it) { return it; } + +template +line_iterator end(line_iterator&) { return agi::line_iterator(); } + template void line_iterator::getline(std::string &str) { union { diff --git a/aegisub/libaegisub/include/libaegisub/log.h b/aegisub/libaegisub/include/libaegisub/log.h index ae5e2b602..197f4539a 100644 --- a/aegisub/libaegisub/include/libaegisub/log.h +++ b/aegisub/libaegisub/include/libaegisub/log.h @@ -16,12 +16,17 @@ /// @brief Logging /// @ingroup libaegisub -#include +#include +#include +#include +#include #include #include #include #include +#include + #ifdef __DEPRECATED // Dodge GCC warnings # undef __DEPRECATED # include @@ -29,8 +34,6 @@ #else # include #endif -#include -#include // These macros below aren't a perm solution, it will depend on how annoying they are through // actual usage, and also depends on msvc support. @@ -144,7 +147,7 @@ class JsonEmitter : public Emitter { agi_timeval time_start; /// Directory to write the log file in - std::string directory; + agi::fs::path directory; /// Parent sink to get messages from const agi::log::LogSink *log_sink; @@ -152,7 +155,7 @@ public: /// Constructor /// @param directory Directory to write the log file in /// @param log_sink Parent sink to get messages from - JsonEmitter(std::string const& directory, const agi::log::LogSink *log_sink); + JsonEmitter(agi::fs::path const& directory, const agi::log::LogSink *log_sink); /// Destructor ~JsonEmitter(); diff --git a/aegisub/libaegisub/include/libaegisub/mru.h b/aegisub/libaegisub/include/libaegisub/mru.h index 62a2f76e9..bccb4701c 100644 --- a/aegisub/libaegisub/include/libaegisub/mru.h +++ b/aegisub/libaegisub/include/libaegisub/mru.h @@ -16,12 +16,13 @@ /// @brief Public interface for MRU (Most Recently Used) lists. /// @ingroup libaegisub +#include #include -#include #include #include #include +#include namespace json { class UnknownElement; @@ -49,11 +50,11 @@ DEFINE_SIMPLE_EXCEPTION_NOINNER(MRUErrorIndexOutOfRange, MRUError, "mru/invalid" class MRUManager { public: /// @brief Map for time->value pairs. - typedef std::list MRUListMap; + typedef std::list MRUListMap; /// @brief Constructor /// @param config File to load MRU values from - MRUManager(std::string const& config, std::string const& default_config, agi::Options *options = 0); + MRUManager(agi::fs::path const& config, std::string const& default_config, agi::Options *options = 0); /// Destructor ~MRUManager(); @@ -62,13 +63,13 @@ public: /// @param key List name /// @param entry Entry to add /// @exception MRUErrorInvalidKey thrown when an invalid key is used. - void Add(std::string const& key, std::string const& entry); + void Add(std::string const& key, agi::fs::path const& entry); /// @brief Remove entry from the list. /// @param key List name /// @param entry Entry to add /// @exception MRUErrorInvalidKey thrown when an invalid key is used. - void Remove(std::string const& key, std::string const& entry); + void Remove(std::string const& key, agi::fs::path const& entry); /// @brief Return list /// @param key List name @@ -79,14 +80,14 @@ public: /// @param key List name /// @param entry 0-base position of entry /// @exception MRUErrorInvalidKey thrown when an invalid key is used. - std::string const& GetEntry(std::string const& key, const size_t entry); + agi::fs::path const& GetEntry(std::string const& key, const size_t entry); /// Write MRU lists to disk. void Flush(); private: /// Internal name of the config file, set during object construction. - const std::string config_name; + const agi::fs::path config_name; /// User preferences object for maximum number of items to list agi::Options *const options; diff --git a/aegisub/libaegisub/include/libaegisub/option.h b/aegisub/libaegisub/include/libaegisub/option.h index f1c486f5d..974ae9824 100644 --- a/aegisub/libaegisub/include/libaegisub/option.h +++ b/aegisub/libaegisub/include/libaegisub/option.h @@ -18,10 +18,12 @@ #pragma once -#include +#include +#include #include #include +#include namespace json { class UnknownElement; @@ -60,7 +62,7 @@ private: OptionValueMap values; /// User config (file that will be written to disk) - const std::string config_file; + const agi::fs::path config_file; /// Settings. const OptionSetting setting; @@ -74,7 +76,7 @@ public: /// @brief Constructor /// @param file User config that will be loaded from and written back to. /// @param default_config Default configuration. - Options(const std::string &file, const std::string &default_config, const OptionSetting setting=NONE); + Options(agi::fs::path const& file, const std::string &default_config, const OptionSetting setting=NONE); /// Destructor ~Options(); diff --git a/aegisub/libaegisub/include/libaegisub/path.h b/aegisub/libaegisub/include/libaegisub/path.h new file mode 100644 index 000000000..cbb10f529 --- /dev/null +++ b/aegisub/libaegisub/include/libaegisub/path.h @@ -0,0 +1,81 @@ +// Copyright (c) 2013, Thomas Goyne +// +// 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/ + +/// @file path.h +/// @brief Common paths. +/// @ingroup libaegisub + +#include +#include + +#include +#include + +namespace agi { +/// Class for handling everything path-related in Aegisub +class Path { + /// Token -> Path map + std::map tokens; + + /// Path -> Token map + std::map paths; + + /// Platform-specific code to fill in the default paths, called in the constructor + void FillPlatformSpecificPaths(); + +public: + /// Constructor + Path(); + + /// Decode and normalize a path which may begin with a registered token + /// @param path Path which is either already absolute or begins with a token + /// @return Absolute path + fs::path Decode(std::string const& path) const; + + /// If path is relative, make it absolute relative to the token's path + /// @param path A possibly relative path + /// @param token Token containing base path for resolving relative paths + /// @return Absolute path if `path` is absolute or `token` is set, `path` otherwise + /// @throws InternalError if `token` is not a valid token name + fs::path MakeAbsolute(fs::path path, std::string const& token) const; + + /// If `token` is set, make `path` relative to it + /// @param path An absolute path + /// @param token Token name to make `path` relative to + /// @return A path relative to `token`'s value if `token` is set, `path` otherwise + /// @throws InternalError if `token` is not a valid token name + fs::path MakeRelative(fs::path const& path, std::string const& token) const; + fs::path MakeRelative(fs::path const& path, const char *token) const { return MakeRelative(path, std::string(token)); } + + /// Make `path` relative to `base`, if possible + /// @param path An absolute path + /// @param base Base path to make `path` relative to + /// @return A path relative to `base`'s value if possible, or `path` otherwise + fs::path MakeRelative(fs::path const& path, fs::path const& base) const; + + /// Encode an absolute path to begin with a token if there are any applicable + /// @param path Absolute path to encode + /// @return path untouched, or with some portion of the beginning replaced with a token + std::string Encode(fs::path const& path) const; + + /// Set a prefix token to use for encoding and decoding paths + /// @param token_name A single word token beginning with '?' + /// @param token_value An absolute path to a directory or file + /// @throws InternalError if `token` is not a valid token name + void SetToken(std::string const& token_name, fs::path const& token_value); +}; + +} diff --git a/aegisub/libaegisub/include/libaegisub/split.h b/aegisub/libaegisub/include/libaegisub/split.h new file mode 100644 index 000000000..43cf0284c --- /dev/null +++ b/aegisub/libaegisub/include/libaegisub/split.h @@ -0,0 +1,45 @@ +// Copyright (c) 2013, Thomas Goyne +// +// 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 +#include + +namespace agi { + typedef boost::iterator_range StringRange; + + template + boost::split_iterator Split(Str const& str, Char delim) { + return boost::make_split_iterator(str, boost::token_finder([=](Char c) { return c == delim; })); + } + + inline std::string str(StringRange const& r) { + return std::string(r.begin(), r.end()); + } +} + +namespace boost { + namespace algorithm { + template + split_iterator begin(split_iterator it) { + return it; + } + + template + split_iterator end(split_iterator) { + return split_iterator(); + } + } +} diff --git a/aegisub/libaegisub/include/libaegisub/thesaurus.h b/aegisub/libaegisub/include/libaegisub/thesaurus.h index 5b551513c..88b2c4360 100644 --- a/aegisub/libaegisub/include/libaegisub/thesaurus.h +++ b/aegisub/libaegisub/include/libaegisub/thesaurus.h @@ -16,10 +16,11 @@ /// @brief MyThes-compatible thesaurus implementation /// @ingroup libaegisub thesaurus -#include +#include "fs_fwd.h" #include #include +#include #include #include @@ -31,9 +32,9 @@ class Thesaurus { /// Map of word -> byte position in the data file std::map offsets; /// Read handle to the data file - scoped_ptr dat; + std::unique_ptr dat; /// Converter from the data file's charset to UTF-8 - scoped_ptr conv; + std::unique_ptr conv; public: /// A pair of a word and synonyms for that word @@ -42,7 +43,7 @@ public: /// Constructor /// @param dat_path Path to data file /// @param idx_path Path to index file - Thesaurus(std::string const& dat_path, std::string const& idx_path); + Thesaurus(agi::fs::path const& dat_path, agi::fs::path const& idx_path); ~Thesaurus(); /// Look up synonyms for a word diff --git a/aegisub/libaegisub/include/libaegisub/util.h b/aegisub/libaegisub/include/libaegisub/util.h index d59020d32..f622dfa95 100644 --- a/aegisub/libaegisub/include/libaegisub/util.h +++ b/aegisub/libaegisub/include/libaegisub/util.h @@ -16,57 +16,31 @@ /// @brief Public interface for general utilities. /// @ingroup libaegisub -#include - -#include #include +#include +#include #include +struct tm; + namespace agi { namespace util { - /// Whether the path is a file or directory. - enum PathType { - TypeFile, ///< File - TypeDir ///< Directory - }; - /// Clamp `b` to the range [`a`,`c`] template inline T mid(T a, T b, T c) { return std::max(a, std::min(b, c)); } - /// Get the parent directory of a path. - /// @param path Path to process. - const std::string DirName(const std::string& path); - - /// Rename a file. - /// @param from Source. - /// @param to Destination. - void Rename(const std::string& from, const std::string& to); - - /// Delete a file - /// @param path Path to file to delete - /// @throws agi::FileNotAccessibleError if file exists but could not be deleted - void Remove(std::string const& path); - /// Get time suitable for logging mechanisms. /// @param tv timeval void time_log(agi_timeval &tv); - /// Make all alphabetic characters lowercase. - /// @param str Input string - void str_lower(std::string &str); - - /// Convert a string to Integer. - /// @param str Input string - int strtoi(std::string const& str); - bool try_parse(std::string const& str, double *out); bool try_parse(std::string const& str, int *out); - /// Check for amount of free space on a Path. - /// @param path[in] Path to check - /// @param type PathType (default is TypeDir) - uint64_t freespace(std::string const& path, PathType type=TypeDir); + /// strftime, but on std::string rather than a fixed buffer + /// @param fmt strftime format string + /// @param tmptr Time to format, or nullptr for current time + /// @return The strftime-formatted string + std::string strftime(const char *fmt, const tm *tmptr = nullptr); struct delete_ptr { template diff --git a/aegisub/libaegisub/include/libaegisub/util_osx.h b/aegisub/libaegisub/include/libaegisub/util_osx.h index 07f34efb0..5ec61a027 100644 --- a/aegisub/libaegisub/include/libaegisub/util_osx.h +++ b/aegisub/libaegisub/include/libaegisub/util_osx.h @@ -28,6 +28,8 @@ /// When linking with this library, be sure to add '-framework CoreFoundation' /// to the GCC commandline. +#ifdef __APPLE__ + #include namespace agi { @@ -91,3 +93,5 @@ std::string OSX_GetBundleAuxillaryExecutablePath(std::string const& executableNa void OSX_OpenLocation(std::string const& location); } // namespace io } // namespace agi + +#endif diff --git a/aegisub/libaegisub/include/libaegisub/util_win.h b/aegisub/libaegisub/include/libaegisub/util_win.h index 72837d84f..2a90ba780 100644 --- a/aegisub/libaegisub/include/libaegisub/util_win.h +++ b/aegisub/libaegisub/include/libaegisub/util_win.h @@ -23,9 +23,6 @@ namespace agi { namespace util { - - std::string ErrorString(DWORD error); - - + std::string ErrorString(DWORD error); } // namespace util } // namespace agi diff --git a/aegisub/libaegisub/include/libaegisub/vfr.h b/aegisub/libaegisub/include/libaegisub/vfr.h index dbe99bff7..4a9947c4e 100644 --- a/aegisub/libaegisub/include/libaegisub/vfr.h +++ b/aegisub/libaegisub/include/libaegisub/vfr.h @@ -24,6 +24,7 @@ #include #include +#include namespace agi { /// Framerate handling. @@ -92,7 +93,7 @@ public: /// not the same thing as CFR X. When timecodes are loaded from a file, /// mkvmerge-style rounding is applied, while setting a constant frame rate /// uses truncation. - Framerate(std::string const& filename); + Framerate(fs::path const& filename); /// @brief CFR constructor /// @param fps Frames per second or 0 for unloaded @@ -195,7 +196,7 @@ public: /// CFR, but saving CFR timecodes is a bit silly). Extra timecodes generated /// to hit length with v2 timecodes will monotonically increase but may not /// be otherwise sensible. - void Save(std::string const& file, int length = -1) const; + void Save(fs::path const& file, int length = -1) const; /// Is this frame rate possibly variable? bool IsVFR() const {return timecodes.size() > 1; } diff --git a/aegisub/libaegisub/lagi_pre.h b/aegisub/libaegisub/lagi_pre.h index 829dca4fc..16776817b 100644 --- a/aegisub/libaegisub/lagi_pre.h +++ b/aegisub/libaegisub/lagi_pre.h @@ -1,5 +1,7 @@ #include "config.h" +#define WIN32_LEAN_AND_MEAN + // Common C #include #include diff --git a/aegisub/libaegisub/unix/access.cpp b/aegisub/libaegisub/unix/access.cpp index f2529fcfe..8562e2cc5 100644 --- a/aegisub/libaegisub/unix/access.cpp +++ b/aegisub/libaegisub/unix/access.cpp @@ -20,59 +20,30 @@ #include "libaegisub/access.h" +#include "libaegisub/fs.h" + #include #include - -#include -#include - #include -#include "libaegisub/util.h" +#include namespace agi { namespace acs { - -void CheckFileRead(const std::string &file) { - Check(file, acs::FileRead); -} - - -void CheckFileWrite(const std::string &file) { - Check(file, acs::FileWrite); -} - - -void CheckDirRead(const std::string &dir) { - Check(dir, acs::DirRead); -} - - -void CheckDirWrite(const std::string &dir) { - Check(dir, acs::DirWrite); -} - - -void Check(const std::string &file, acs::Type type) { +void Check(agi::fs::path const& file, acs::Type type) { struct stat file_stat; - int file_status; - file_status = stat(file.c_str(), &file_stat); + int file_status = stat(file.c_str(), &file_stat); if (file_status != 0) { switch (errno) { case ENOENT: - throw FileNotFoundError("File or path not found."); - break; - + throw fs::FileNotFound(file); case EACCES: - throw Read("Access Denied to file, path or path component."); - break; - + throw fs::ReadDenied(file); case EIO: - throw Fatal("Fatal I/O error occurred."); - break; + throw fs::FileSystemUnknownError("Fatal I/O error in 'stat' on path: " + file.string()); } } @@ -80,24 +51,24 @@ void Check(const std::string &file, acs::Type type) { case FileRead: case FileWrite: if ((file_stat.st_mode & S_IFREG) == 0) - throw NotAFile("Not a file."); + throw fs::NotAFile(file); break; case DirRead: case DirWrite: if ((file_stat.st_mode & S_IFDIR) == 0) - throw NotADirectory("Not a directory."); + throw fs::NotADirectory(file); break; } file_status = access(file.c_str(), R_OK); if (file_status != 0) - throw Read("File or directory is not readable."); + throw fs::ReadDenied(file); if (type == DirWrite || type == FileWrite) { file_status = access(file.c_str(), W_OK); if (file_status != 0) - throw Write("File or directory is not writable."); + throw fs::WriteDenied(file); } } diff --git a/aegisub/libaegisub/unix/fs.cpp b/aegisub/libaegisub/unix/fs.cpp new file mode 100644 index 000000000..63c22450e --- /dev/null +++ b/aegisub/libaegisub/unix/fs.cpp @@ -0,0 +1,92 @@ +// Copyright (c) 2013, Thomas Goyne +// +// 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 "config.h" + +#include "libaegisub/fs.h" + +#include +#include +#include +#include + +namespace bfs = boost::filesystem; + +namespace agi { namespace fs { +std::string ShortName(path const& p) { + return p.string(); +} + +void Touch(path const& file) { + CreateDirectory(file.parent_path()); + + int fd = open(file.c_str(), O_CREAT | O_APPEND | O_WRONLY, 0644); + if (fd >= 0) { + futimes(fd, nullptr); + close(fd); + } +} + +struct DirectoryIterator::PrivData { + boost::system::error_code ec; + bfs::directory_iterator it; + std::string filter; + PrivData(path const& p, std::string const& filter) : it(p, ec), filter(filter) { } + + bool bad() const { + return + it == bfs::directory_iterator() || + (!filter.empty() && fnmatch(filter.c_str(), it->path().filename().c_str(), 0)); + } +}; + +DirectoryIterator::DirectoryIterator() { } +DirectoryIterator::DirectoryIterator(path const& p, std::string const& filter) +: privdata(new PrivData(p, filter)) +{ + if (privdata->it == bfs::directory_iterator()) + privdata.reset(); + else if (privdata->bad()) + ++*this; + else + value = privdata->it->path().filename().string(); +} + +bool DirectoryIterator::operator==(DirectoryIterator const& rhs) const { + return privdata.get() == rhs.privdata.get(); +} + +DirectoryIterator& DirectoryIterator::operator++() { + if (!privdata) return *this; + + ++privdata->it; + + while (privdata->bad()) { + if (privdata->it == bfs::directory_iterator()) { + privdata.reset(); + return *this; + } + ++privdata->it; + } + + value = privdata->it->path().filename().string(); + + return *this; +} + +DirectoryIterator::~DirectoryIterator() { } + +} } diff --git a/aegisub/libaegisub/unix/log.cpp b/aegisub/libaegisub/unix/log.cpp index 5d43807b0..50d86821d 100644 --- a/aegisub/libaegisub/unix/log.cpp +++ b/aegisub/libaegisub/unix/log.cpp @@ -44,8 +44,8 @@ void EmitSTDOUT::log(SinkMessage *sm) { sm->file, sm->func, sm->line, - (int)sm->len, - sm->message); + (int)sm->message.size(), + sm->message.c_str()); if (!isatty(fileno(stdout))) fflush(stdout); } diff --git a/aegisub/libaegisub/unix/path.cpp b/aegisub/libaegisub/unix/path.cpp new file mode 100644 index 000000000..994f5595c --- /dev/null +++ b/aegisub/libaegisub/unix/path.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2013, Thomas Goyne +// +// 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 + +#include + +#include +#include + +namespace { +std::string home_dir() { + const char *env = getenv("HOME"); + if (env) return env; + + if ((env = getenv("USER")) || (env = getenv("LOGNAME"))) { + if (passwd *user_info = getpwnam(env)) + return user_info->pw_dir; + } + + throw agi::EnvironmentError("Could not get home directory. Make sure HOME is set."); +} + +std::string data_dir() { +#ifndef __APPLE__ + return P_DATA; +#else + return agi::util::OSX_GetBundleSharedSupportDirectory(); +#endif +} + +} + +namespace agi { + +void Path::FillPlatformSpecificPaths() { + agi::fs::path home = home_dir(); + SetToken("?user", home/".aegisub"); + SetToken("?local", home/".aegisub"); + SetToken("?data", data_dir()); + SetToken("?temp", boost::filesystem::temp_directory_path()); + SetToken("?dictionary", "/usr/share/hunspell"); + SetToken("?docs", P_DOC); +} + +} diff --git a/aegisub/libaegisub/unix/util.cpp b/aegisub/libaegisub/unix/util.cpp index ea556ec6e..4511637b0 100644 --- a/aegisub/libaegisub/unix/util.cpp +++ b/aegisub/libaegisub/unix/util.cpp @@ -20,72 +20,10 @@ #include "libaegisub/util.h" -#include "libaegisub/access.h" - -#include -#include -#include -#ifdef HAVE_SYS_TIME_H -#include -#else -#include -#endif - -#include -#include - -#include - -namespace agi { - namespace util { - - -const std::string DirName(const std::string& path) { - if (path.find('/') == std::string::npos) { - return "."; - } - - return path.substr(0, path.rfind("/")+1); -} - -void Rename(const std::string& from, const std::string& to) { - acs::CheckFileWrite(from); - - try { - acs::CheckFileWrite(to); - } catch (FileNotFoundError const&) { - acs::CheckDirWrite(DirName(to)); - } - - rename(from.c_str(), to.c_str()); -} - -void Remove(std::string const& path) { - if (!remove(path.c_str()) && errno != ENOENT) - throw agi::FileNotAccessibleError("Can not remove file: " + path); -} +namespace agi { namespace util { void time_log(timeval &tv) { gettimeofday(&tv, (struct timezone *)NULL); } -uint64_t freespace(std::string const& path, PathType type) { - struct statvfs fs; - std::string check(path); - - if (type == TypeFile) - check.assign(DirName(path)); - - acs::CheckDirRead(check); - - if ((statvfs(check.c_str(), &fs)) == 0) { - return (uint64_t)fs.f_bsize * fs.f_bavail; - } else { - /// @todo We need a collective set of exceptions for ENOTDIR, EIO etc. - throw("Failed getting free space"); - } -} - - - } // namespace io -} // namespace agi +} } diff --git a/aegisub/libaegisub/windows/access.cpp b/aegisub/libaegisub/windows/access.cpp index 394eeece9..62181d01b 100644 --- a/aegisub/libaegisub/windows/access.cpp +++ b/aegisub/libaegisub/windows/access.cpp @@ -16,18 +16,19 @@ /// @brief Windows access methods. /// @ingroup libaegisub windows -#include - -#include -#include #include -#include +#include #include #include #include +#include +#include + +#include + namespace { bool check_permission(bool is_read, SECURITY_DESCRIPTOR *sd, HANDLE client_token) { DWORD access_check = is_read ? FILE_READ_DATA : FILE_APPEND_DATA | FILE_WRITE_DATA; @@ -48,43 +49,23 @@ namespace { namespace agi { namespace acs { -void CheckFileRead(const std::string &file) { - Check(file, acs::FileRead); -} - -void CheckFileWrite(const std::string &file) { - Check(file, acs::FileWrite); -} - -void CheckDirRead(const std::string &dir) { - Check(dir, acs::DirRead); -} - -void CheckDirWrite(const std::string &dir) { - Check(dir, acs::DirWrite); -} - /* This function is still a proof of concept, it's probably rife with bugs, below is a short (and incomplete) todo * "Basic" checks (Read/Write/File/Dir) checks for FAT32 filesystems which requires detecting the filesystem being used. */ -void Check(const std::string &file, acs::Type type) { - std::wstring wfile = agi::charset::ConvertW(file); - - DWORD file_attr = GetFileAttributes(wfile.c_str()); +void Check(fs::path const& file, acs::Type type) { + DWORD file_attr = GetFileAttributes(file.c_str()); if ((file_attr & INVALID_FILE_ATTRIBUTES) == INVALID_FILE_ATTRIBUTES) { switch (GetLastError()) { case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: - throw FileNotFoundError(file); - + throw fs::FileNotFound(file); case ERROR_ACCESS_DENIED: - throw Read("Access denied to file or path component"); - + throw fs::ReadDenied(file); default: - throw Fatal("Fatal I/O error occurred."); + throw fs::FileSystemUnknownError(str(boost::format("Unexpected error when getting attributes for \"%s\": %s") % file % util::ErrorString(GetLastError()))); } } @@ -92,25 +73,25 @@ void Check(const std::string &file, acs::Type type) { case FileRead: case FileWrite: if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) - throw NotAFile(file + " is not a file"); + throw fs::NotAFile(file); break; case DirRead: case DirWrite: if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY) - throw NotADirectory(file + " is not a directory"); + throw fs::NotADirectory(file); break; } SECURITY_INFORMATION info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION; DWORD len = 0; - GetFileSecurity(wfile.c_str(), info, nullptr, 0, &len); + GetFileSecurity(file.c_str(), info, nullptr, 0, &len); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) LOG_W("acs/check") << "GetFileSecurity: fatal: " << util::ErrorString(GetLastError()); std::vector sd_buff(len); SECURITY_DESCRIPTOR *sd = (SECURITY_DESCRIPTOR *)&sd_buff[0]; - if (!GetFileSecurity(wfile.c_str(), info, sd, len, &len)) + if (!GetFileSecurity(file.c_str(), info, sd, len, &len)) LOG_W("acs/check") << "GetFileSecurity failed: " << util::ErrorString(GetLastError()); ImpersonateSelf(SecurityImpersonation); @@ -119,9 +100,9 @@ void Check(const std::string &file, acs::Type type) { LOG_W("acs/check") << "OpenThreadToken failed: " << util::ErrorString(GetLastError()); if (!check_permission(true, sd, client_token)) - throw Read("File or directory is not readable"); + throw fs::ReadDenied(file); if ((type == DirWrite || type == FileWrite) && !check_permission(false, sd, client_token)) - throw Write("File or directory is not writable"); + throw fs::WriteDenied(file); } } // namespace Access diff --git a/aegisub/libaegisub/windows/charset_conv_win.cpp b/aegisub/libaegisub/windows/charset_conv_win.cpp index ebfe9340d..67385001a 100644 --- a/aegisub/libaegisub/windows/charset_conv_win.cpp +++ b/aegisub/libaegisub/windows/charset_conv_win.cpp @@ -18,6 +18,18 @@ #include +namespace { +std::string from_w(agi::charset::IconvWrapper &w32Conv, std::wstring const& source) { + std::string dest; + size_t srcLen = source.size() * sizeof(wchar_t); + const char* src = reinterpret_cast(source.c_str()); + size_t len = w32Conv.RequiredBufferSize(src, srcLen); + dest.resize(len); + w32Conv.Convert(src, srcLen, &dest[0], len); + return dest; +} +} + namespace agi { namespace charset { @@ -33,14 +45,12 @@ std::wstring ConvertW(std::string const& source) { std::string ConvertW(std::wstring const& source) { static IconvWrapper w32Conv("utf-16le", "utf-8", false); + return from_w(w32Conv, source); +} - std::string dest; - size_t srcLen = source.size() * sizeof(wchar_t); - const char* src = reinterpret_cast(source.c_str()); - size_t len = w32Conv.RequiredBufferSize(src, srcLen); - dest.resize(len); - w32Conv.Convert(src, srcLen, &dest[0], len); - return dest; +std::string ConvertLocal(std::wstring const& source) { + static IconvWrapper w32Conv("utf-16le", "char", false); + return from_w(w32Conv, source); } } diff --git a/aegisub/libaegisub/windows/fs.cpp b/aegisub/libaegisub/windows/fs.cpp new file mode 100644 index 000000000..b075775e5 --- /dev/null +++ b/aegisub/libaegisub/windows/fs.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2013, Thomas Goyne +// +// 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 "config.h" + +#include "libaegisub/fs.h" + +#include "libaegisub/access.h" +#include "libaegisub/charset_conv_win.h" +#include "libaegisub/exception.h" +#include "libaegisub/scoped_ptr.h" +#include "libaegisub/util_win.h" + +using agi::charset::ConvertW; +using agi::charset::ConvertLocal; + +#include +namespace bfs = boost::filesystem; + +#undef CreateDirectory + +namespace agi { namespace fs { +std::string ShortName(path const& p) { + std::wstring out(MAX_PATH + 1, 0); + DWORD len = GetShortPathName(p.c_str(), &out[0], out.size()); + if (!len) + return p.string(); + out.resize(len); + return ConvertLocal(out); +} + +void Touch(path const& file) { + CreateDirectory(file.parent_path()); + + SYSTEMTIME st; + FILETIME ft; + GetSystemTime(&st); + if(!SystemTimeToFileTime(&st, &ft)) + throw EnvironmentError("SystemTimeToFileTime failed with error: " + util::ErrorString(GetLastError())); + + scoped_holder + h(CreateFile(file.c_str(), GENERIC_WRITE, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr), CloseHandle); + // error handling etc. + if (!SetFileTime(h, nullptr, nullptr, &ft)) + throw EnvironmentError("SetFileTime failed with error: " + util::ErrorString(GetLastError())); +} + +struct DirectoryIterator::PrivData { + scoped_holder h; + PrivData() : h(INVALID_HANDLE_VALUE, FindClose) { } +}; + +DirectoryIterator::DirectoryIterator() { } +DirectoryIterator::DirectoryIterator(path const& p, std::string const& filter) +: privdata(new PrivData) +{ + WIN32_FIND_DATA data; + privdata->h = FindFirstFileEx((p/(filter.empty() ? "*.*" : filter)).c_str(), FindExInfoBasic, &data, FindExSearchNameMatch, nullptr, 0); + if (privdata->h == INVALID_HANDLE_VALUE) { + privdata.reset(); + return; + } + + value = ConvertW(data.cFileName); + while (value[0] == '.' && (value[1] == 0 || value[1] == '.')) + ++*this; +} + +bool DirectoryIterator::operator==(DirectoryIterator const& rhs) const { + return privdata.get() == rhs.privdata.get(); +} + +DirectoryIterator& DirectoryIterator::operator++() { + WIN32_FIND_DATA data; + if (FindNextFile(privdata->h, &data)) + value = ConvertW(data.cFileName); + else { + privdata.reset(); + value.clear(); + } + return *this; +} + +DirectoryIterator::~DirectoryIterator() { } + +} } diff --git a/aegisub/libaegisub/windows/path_win.cpp b/aegisub/libaegisub/windows/path_win.cpp new file mode 100644 index 000000000..f5f5e5a3b --- /dev/null +++ b/aegisub/libaegisub/windows/path_win.cpp @@ -0,0 +1,57 @@ +// Copyright (c) 2013, Thomas Goyne +// +// 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/ + +/// @file path.cpp +/// @brief Windows-specific path code +/// @ingroup libaegisub windows + +#include + +#include + +#include + +namespace { +#include +#include + +agi::fs::path WinGetFolderPath(int folder) { + wchar_t path[MAX_PATH+1] = {0}; + if (FAILED(SHGetFolderPathW(0, folder, 0, 0, path))) + throw agi::EnvironmentError("SHGetFolderPath failed. This should not happen."); + return path; +} +} + +namespace agi { + +void Path::FillPlatformSpecificPaths() { + tokens["?temp"] = boost::filesystem::temp_directory_path(); + + SetToken("?user", WinGetFolderPath(CSIDL_APPDATA)/"Aegisub"); + SetToken("?local", WinGetFolderPath(CSIDL_LOCAL_APPDATA)/"Aegisub"); + + /// @todo error checking + int argc; + LPWSTR *argv = CommandLineToArgvW(L"", &argc); + SetToken("?data", argv[0]); + LocalFree(argv); + + SetToken("?dictionary", Decode("?data/dictionaries")); + //SetToken("?docs", Decode("?data/docs")); +} + +} diff --git a/aegisub/libaegisub/windows/util_win.cpp b/aegisub/libaegisub/windows/util_win.cpp index 995089546..b416cb089 100644 --- a/aegisub/libaegisub/windows/util_win.cpp +++ b/aegisub/libaegisub/windows/util_win.cpp @@ -16,55 +16,18 @@ /// @brief Windows utility methods. /// @ingroup libaegisub windows -#include -#include +#include "libaegisub/util_win.h" #include -#include -#include - -#include "libaegisub/access.h" #include "libaegisub/charset_conv_win.h" #include "libaegisub/types.h" -#include "libaegisub/util.h" -#include "libaegisub/util_win.h" namespace agi { namespace util { using agi::charset::ConvertW; -const std::string DirName(const std::string& path) { - std::string::size_type pos = path.rfind('/'); - - if (pos == std::string::npos) pos = path.rfind('\\'); - if (pos == std::string::npos) return "."; - - return path.substr(0, pos+1); -} - -void Rename(const std::string& from, const std::string& to) { - acs::CheckFileWrite(from); - - try { - acs::CheckFileWrite(to); - } catch (FileNotFoundError const&) { - acs::CheckDirWrite(DirName(to)); - } - - if (!MoveFileEx(ConvertW(from).c_str(), ConvertW(to).c_str(), MOVEFILE_REPLACE_EXISTING)) - throw agi::FileNotAccessibleError("Can not overwrite file: " + ErrorString(GetLastError())); -} - -void Remove(std::string const& path) { - if (!DeleteFile(ConvertW(path).c_str())) { - DWORD err = GetLastError(); - if (err != ERROR_FILE_NOT_FOUND) - throw agi::FileNotAccessibleError("Can not remove file: " + ErrorString(err)); - } -} - std::string ErrorString(DWORD error) { LPWSTR lpstr = nullptr; @@ -116,19 +79,5 @@ void time_log(agi_timeval &tv) { tv.tv_usec = (long)(tmpres % 1000000UL); } -uint64_t freespace(std::string const& path, PathType type) { - if (type == TypeFile) - return freespace(DirName(path)); - - ULARGE_INTEGER bytes_available; - if (GetDiskFreeSpaceEx(ConvertW(path).c_str(), &bytes_available, 0, 0)) - return bytes_available.QuadPart; - - acs::CheckDirRead(path); - - /// @todo GetLastError -> Exception mapping - throw "Unknown error getting free space"; -} - } // namespace io } // namespace agi diff --git a/aegisub/src/Makefile b/aegisub/src/Makefile index ad9f0d5ac..dccf25d72 100644 --- a/aegisub/src/Makefile +++ b/aegisub/src/Makefile @@ -157,7 +157,6 @@ SRC += \ auto4_base.cpp \ avisynth_wrap.cpp \ base_grid.cpp \ - charset_conv.cpp \ charset_detect.cpp \ colorspace.cpp \ colour_button.cpp \ diff --git a/aegisub/src/aegisublocale.cpp b/aegisub/src/aegisublocale.cpp index f6957b274..4bcb1ccb8 100644 --- a/aegisub/src/aegisublocale.cpp +++ b/aegisub/src/aegisublocale.cpp @@ -36,18 +36,16 @@ #include "aegisublocale.h" +#include "standard_paths.h" + #include +#include #include #include -#include -#include #include -#include #include // Keep this last so wxUSE_CHOICEDLG is set. -#include "standard_paths.h" - #ifndef AEGISUB_CATALOG #define AEGISUB_CATALOG "aegisub" #endif @@ -56,7 +54,7 @@ wxTranslations *AegisubLocale::GetTranslations() { wxTranslations *translations = wxTranslations::Get(); if (!translations) { wxTranslations::Set(translations = new wxTranslations); - wxFileTranslationsLoader::AddCatalogLookupPathPrefix(StandardPaths::DecodePath("?data/locale/")); + wxFileTranslationsLoader::AddCatalogLookupPathPrefix(StandardPaths::DecodePath("?data/locale/").wstring()); } return translations; } diff --git a/aegisub/src/agi_pre.h b/aegisub/src/agi_pre.h index 125b081bf..d50e48675 100644 --- a/aegisub/src/agi_pre.h +++ b/aegisub/src/agi_pre.h @@ -74,7 +74,6 @@ #include #include #include -#include // wxWidgets headers #include // Leave this first. diff --git a/aegisub/src/ass_attachment.cpp b/aegisub/src/ass_attachment.cpp index 1fb02a663..7b40d597e 100644 --- a/aegisub/src/ass_attachment.cpp +++ b/aegisub/src/ass_attachment.cpp @@ -34,26 +34,33 @@ #include "config.h" -#include - -#include - #include "ass_attachment.h" -#include "compat.h" - #include -#include -AssAttachment::AssAttachment(wxString const& name, AssEntryGroup group) +#include +#include + +AssAttachment::AssAttachment(std::string const& name, AssEntryGroup group) : data(new std::vector) , filename(name) , group(group) { - wxFileName fname(filename); - wxString ext = fname.GetExt().Lower(); - if (ext == "ttf") - filename = fname.GetName() + "_0." + ext; +} + +AssAttachment::AssAttachment(agi::fs::path const& name, AssEntryGroup group) +: data(new std::vector) +, filename(name.filename().string()) +, group(group) +{ + if (boost::iends_with(filename, ".ttf")) + filename = filename.substr(0, filename.size() - 4) + "_0" + filename.substr(filename.size() - 4); + + std::unique_ptr file(agi::io::Open(name, true)); + file->seekg(0, std::ios::end); + data->resize(file->tellg()); + file->seekg(0, std::ios::beg); + file->read(&(*data)[0], data->size()); } AssEntry *AssAttachment::Clone() const { @@ -62,46 +69,27 @@ AssEntry *AssAttachment::Clone() const { return clone; } -const wxString AssAttachment::GetEntryData() const { - size_t pos = 0; +const std::string AssAttachment::GetEntryData() const { size_t size = data->size(); size_t written = 0; - unsigned char src[3]; - unsigned char dst[4]; - // Write header - wxString entryData = (group == ENTRY_FONT ? "fontname: " : "filename: ") + filename + "\r\n"; + std::string entryData = (group == ENTRY_FONT ? "fontname: " : "filename: ") + filename + "\r\n"; + entryData.reserve(size * 4 / 3 + size / 80 * 2 + entryData.size() + 2); - // Read three bytes - while (pos < size) { - // Number to read - size_t read = size - pos; - if (read > 3) read = 3; + for (size_t pos = 0; pos < size; pos += 3) { + unsigned char src[3] = { '\0', '\0', '\0' }; + memcpy(src, &(*data)[pos], std::min(3u, size - pos)); - // Read source - src[0] = (*data)[pos]; - if (read >= 2) src[1] = (*data)[pos+1]; - else src[1] = 0; - if (read == 3) src[2] = (*data)[pos+2]; - else src[2] = 0; - pos += read; - - // Codify + unsigned char dst[4]; dst[0] = src[0] >> 2; dst[1] = ((src[0] & 0x3) << 4) | ((src[1] & 0xF0) >> 4); dst[2] = ((src[1] & 0xF) << 2) | ((src[2] & 0xC0) >> 6); dst[3] = src[2] & 0x3F; - // Number to write - size_t toWrite = read+1; + for (size_t i = 0; i < std::min(size - pos + 1, 4u); ++i) { + entryData += dst[i] + 33; - // Convert to text - for (size_t i=0;isize()); +void AssAttachment::Extract(agi::fs::path const& filename) const { + agi::io::Save(filename, true).Get().write(&(*data)[0], data->size()); } -void AssAttachment::Import(wxString const& filename) { - agi::scoped_ptr file(agi::io::Open(from_wx(filename), true)); - file->seekg(0, std::ios::end); - data->resize(file->tellg()); - file->seekg(0, std::ios::beg); - file->read(&(*data)[0], data->size()); -} - -wxString AssAttachment::GetFileName(bool raw) const { - if (raw || filename.Right(4).Lower() != ".ttf") return filename; +std::string AssAttachment::GetFileName(bool raw) const { + if (raw || !boost::iends_with(filename, ".ttf")) return filename; // Remove stuff after last underscore if it's a font - wxString::size_type last_under = filename.rfind('_'); - if (last_under == wxString::npos) + std::string::size_type last_under = filename.rfind('_'); + if (last_under == std::string::npos) return filename; - return filename.Left(last_under) + ".ttf"; + return filename.substr(0, last_under) + ".ttf"; } void AssAttachment::Finish() { - // Source and dest buffers unsigned char src[4]; unsigned char dst[3]; data->reserve(buffer.size() * 3 / 4); - // Read buffer for(size_t pos = 0; pos + 1 < buffer.size(); ) { - // Find characters left size_t read = std::min(buffer.size() - pos, 4); // Move 4 bytes from buffer to src @@ -157,11 +134,9 @@ void AssAttachment::Finish() { dst[1] = ((src[1] & 0xF) << 4) | (src[2] >> 2); dst[2] = ((src[2] & 0x3) << 6) | (src[3]); - // Push into vector copy(dst, dst + read - 1, back_inserter(*data)); } - // Clear buffer buffer.clear(); - buffer.Shrink(); + buffer.shrink_to_fit(); } diff --git a/aegisub/src/ass_attachment.h b/aegisub/src/ass_attachment.h index d9702c3d7..06ac842ac 100644 --- a/aegisub/src/ass_attachment.h +++ b/aegisub/src/ass_attachment.h @@ -32,21 +32,23 @@ /// @ingroup subs_storage /// +#include "ass_entry.h" + +#include + #include #include -#include "ass_entry.h" - /// @class AssAttachment class AssAttachment : public AssEntry { /// Decoded file data std::shared_ptr> data; /// Encoded data which has been read from the script but not yet decoded - wxString buffer; + std::vector buffer; /// Name of the attached file, with SSA font mangling if it is a ttf - wxString filename; + std::string filename; AssEntryGroup group; @@ -56,25 +58,22 @@ public: /// Add a line of data (without newline) read from a subtitle file to the /// buffer waiting to be decoded - void AddData(wxString const& data) { buffer += data; } + void AddData(std::string const& data) { buffer.insert(buffer.end(), data.begin(), data.end()); } /// Decode all data passed with AddData void Finish(); /// Extract the contents of this attachment to a file /// @param filename Path to save the attachment to - void Extract(wxString const& filename) const; - - /// Import the contents of a file as an attachment - /// @param filename Path to import - void Import(wxString const& filename); + void Extract(agi::fs::path const& filename) const; /// Get the name of the attached file /// @param raw If false, remove the SSA filename mangling - wxString GetFileName(bool raw=false) const; + std::string GetFileName(bool raw=false) const; - const wxString GetEntryData() const override; + const std::string GetEntryData() const override; AssEntryGroup Group() const override { return group; } AssEntry *Clone() const override; - AssAttachment(wxString const& name, AssEntryGroup group); + AssAttachment(std::string const& name, AssEntryGroup group); + AssAttachment(agi::fs::path const& name, AssEntryGroup group); }; diff --git a/aegisub/src/ass_dialogue.cpp b/aegisub/src/ass_dialogue.cpp index ea6182596..418724962 100644 --- a/aegisub/src/ass_dialogue.cpp +++ b/aegisub/src/ass_dialogue.cpp @@ -34,27 +34,24 @@ #include "config.h" #include "ass_dialogue.h" -#include "compat.h" #include "subtitle_format.h" #include "utils.h" #include +#include +#include #include +#include +#include #include -#include - -#include -#include +#include +#include using namespace boost::adaptors; static int next_id = 0; -std::size_t hash_value(wxString const& s) { - return wxStringHash()(s); -} - AssDialogue::AssDialogue() : Id(++next_id) , Comment(false) @@ -80,105 +77,91 @@ AssDialogue::AssDialogue(AssDialogue const& that) memmove(Margin, that.Margin, sizeof Margin); } -AssDialogue::AssDialogue(wxString const& data) +AssDialogue::AssDialogue(std::string const& data) : Id(++next_id) { - if (!Parse(data)) - throw SubtitleFormatParseError(from_wx("Failed parsing line: " + data), 0); + Parse(data); } AssDialogue::~AssDialogue () { } -bool AssDialogue::Parse(wxString const& rawData) { - size_t pos = 0; - wxString temp; +class tokenizer { + agi::StringRange str; + boost::split_iterator pos; - // Get type - if (rawData.StartsWith("Dialogue:")) { +public: + tokenizer(agi::StringRange const& str) : str(str) , pos(agi::Split(str, ',')) { } + + agi::StringRange next_tok() { + if (pos.eof()) + throw SubtitleFormatParseError("Failed parsing line: " + std::string(str.begin(), str.end()), 0); + return *pos++; + } + + std::string next_str() { return agi::str(next_tok()); } + std::string next_str_trim() { return agi::str(boost::trim_copy(next_tok())); } +}; + +void AssDialogue::Parse(std::string const& raw) { + agi::StringRange str; + if (boost::starts_with(raw, "Dialogue:")) { Comment = false; - pos = 10; + str = agi::StringRange(raw.begin() + 10, raw.end()); } - else if (rawData.StartsWith("Comment:")) { + else if (boost::starts_with(raw, "Comment:")) { Comment = true; - pos = 9; + str = agi::StringRange(raw.begin() + 9, raw.end()); } - else return false; + else + throw SubtitleFormatParseError("Failed parsing line: " + raw, 0); - wxStringTokenizer tkn(rawData.Mid(pos),",",wxTOKEN_RET_EMPTY_ALL); - if (!tkn.HasMoreTokens()) return false; + tokenizer tkn(str); // Get first token and see if it has "Marked=" in it - temp = tkn.GetNextToken().Trim(false).Trim(true); - bool ssa = temp.Lower().StartsWith("marked="); + auto tmp = tkn.next_str_trim(); + bool ssa = boost::istarts_with(tmp, "marked="); // Get layer number if (ssa) Layer = 0; - else { - long templ; - temp.ToLong(&templ); - Layer = templ; - } + else + Layer = boost::lexical_cast(tmp); - // Get start time - if (!tkn.HasMoreTokens()) return false; - Start = tkn.GetNextToken(); - - // Get end time - if (!tkn.HasMoreTokens()) return false; - End = tkn.GetNextToken(); - - // Get style - if (!tkn.HasMoreTokens()) return false; - Style = tkn.GetNextToken().Trim(true).Trim(false); - - // Get actor - if (!tkn.HasMoreTokens()) return false; - Actor = tkn.GetNextToken().Trim(true).Trim(false); - - // Get margins - for (int i = 0; i < 3; ++i) { - if (!tkn.HasMoreTokens()) return false; - SetMarginString(tkn.GetNextToken().Trim(false).Trim(true), i); - } - - if (!tkn.HasMoreTokens()) return false; - Effect = tkn.GetNextToken().Trim(true).Trim(false); - - // Get text - Text = rawData.Mid(pos + tkn.GetPosition()); - - return true; + Start = tkn.next_str_trim(); + End = tkn.next_str_trim(); + Style = tkn.next_str_trim(); + Actor = tkn.next_str_trim(); + for (int& margin : Margin) + margin = mid(0, boost::lexical_cast(tkn.next_str()), 9999); + Effect = tkn.next_str_trim(); + Text = std::string(tkn.next_tok().begin(), str.end()); } -static void append_int(wxString &str, int v) { - str += std::to_wstring(v); +void append_int(std::string &str, int v) { + boost::spirit::karma::generate(back_inserter(str), boost::spirit::karma::int_, v); str += ','; } -static void append_str(wxString &out, wxString const& str) { +void append_str(std::string &out, std::string const& str) { out += str; out += ','; } -static void append_unsafe_str(wxString &out, wxString const& str) { +void append_unsafe_str(std::string &out, std::string const& str) { if (str.find(',') == str.npos) out += str; - else { - wxString c = str; - c.Replace(wxS(","), wxS(";")); - out += c; - } + else + out += boost::replace_all_copy(str, ",", ";"); out += ','; } -wxString AssDialogue::GetData(bool ssa) const { - wxString str = Comment ? wxS("Comment: ") : wxS("Dialogue: "); +std::string AssDialogue::GetData(bool ssa) const { + std::string str = Comment ? "Comment: " : "Dialogue: "; str.reserve(51 + Style.get().size() + Actor.get().size() + Effect.get().size() + Text.get().size()); if (ssa) - append_str(str, wxS("Marked=0")); + append_str(str, "Marked=0"); else append_int(str, Layer); append_str(str, Start.GetAssFormated()); @@ -190,20 +173,19 @@ wxString AssDialogue::GetData(bool ssa) const { append_unsafe_str(str, Effect); str += Text.get(); - // Make sure that final has no line breaks if (str.find('\n') != str.npos || str.find('\r') != str.npos) { - str.Replace("\n", ""); - str.Replace("\r", ""); + boost::replace_all(str, "\n", ""); + boost::replace_all(str, "\r", ""); } return str; } -const wxString AssDialogue::GetEntryData() const { +const std::string AssDialogue::GetEntryData() const { return GetData(false); } -wxString AssDialogue::GetSSAText() const { +std::string AssDialogue::GetSSAText() const { return GetData(true); } @@ -217,7 +199,7 @@ std::auto_ptr> AssDialogue::ParseTags() cons } int drawingLevel = 0; - std::string text(from_wx(Text.get())); + std::string const& text(Text.get()); for (size_t len = text.size(), cur = 0; cur < len; ) { // Overrides block @@ -284,32 +266,17 @@ void AssDialogue::StripTags() { static std::string get_text(AssDialogueBlock &d) { return d.GetText(); } void AssDialogue::UpdateText(boost::ptr_vector& blocks) { if (blocks.empty()) return; - Text = to_wx(join(blocks | transformed(get_text), "")); + Text = join(blocks | transformed(get_text), ""); } -void AssDialogue::SetMarginString(wxString const& origvalue, int which) { +void AssDialogue::SetMarginString(std::string const& origvalue, int which) { if (which < 0 || which > 2) throw InvalidMarginIdError(); - - // Make it numeric - wxString strvalue = origvalue; - if (!strvalue.IsNumber()) { - strvalue.clear(); - for (size_t i = 0; i < origvalue.Length(); ++i) { - if (origvalue.Mid(i, 1).IsNumber()) { - strvalue += origvalue.Mid(i, 1); - } - } - } - - // Get value - long value = 0; - strvalue.ToLong(&value); - Margin[which] = mid(0, value, 9999); + Margin[which] = mid(0, atoi(origvalue.c_str()), 9999); } -wxString AssDialogue::GetMarginString(int which) const { +std::string AssDialogue::GetMarginString(int which) const { if (which < 0 || which > 2) throw InvalidMarginIdError(); - return wxString::Format("%d", Margin[which]); + return std::to_string(Margin[which]); } bool AssDialogue::CollidesWith(const AssDialogue *target) const { @@ -318,10 +285,9 @@ bool AssDialogue::CollidesWith(const AssDialogue *target) const { } static std::string get_text_p(AssDialogueBlock *d) { return d->GetText(); } -wxString AssDialogue::GetStrippedText() const { - wxString ret; +std::string AssDialogue::GetStrippedText() const { boost::ptr_vector blocks(ParseTags()); - return to_wx(join(blocks | agi::of_type() | transformed(get_text_p), "")); + return join(blocks | agi::of_type() | transformed(get_text_p), ""); } AssEntry *AssDialogue::Clone() const { @@ -336,10 +302,9 @@ void AssDialogueBlockDrawing::TransformCoords(int mx, int my, double x, double y bool is_x = true; std::string final; - boost::char_separator sep(" "); - for (auto const& cur : boost::tokenizer>(text, sep)) { + for (auto const& cur : agi::Split(text, ' ')) { if (std::all_of(begin(cur), end(cur), isdigit)) { - int val = boost::lexical_cast(cur); + int val = boost::lexical_cast(agi::str(cur)); if (is_x) val = (int)((val + mx) * x + .5); else diff --git a/aegisub/src/ass_dialogue.h b/aegisub/src/ass_dialogue.h index 89bd17f38..4ee3a1690 100644 --- a/aegisub/src/ass_dialogue.h +++ b/aegisub/src/ass_dialogue.h @@ -50,8 +50,6 @@ enum AssBlockType { BLOCK_DRAWING }; -std::size_t hash_value(wxString const& s); - /// @class AssDialogueBlock /// @brief AssDialogue Blocks /// @@ -126,7 +124,11 @@ public: }; class AssDialogue : public AssEntry { - wxString GetData(bool ssa) const; + std::string GetData(bool ssa) const; + + /// @brief Parse raw ASS data into everything else + /// @param data ASS line + void Parse(std::string const& data); public: /// Unique ID of this line. Copies of the line for Undo/Redo purposes /// preserve the unique ID, so that the equivalent lines can be found in @@ -144,21 +146,16 @@ public: /// Ending time AssTime End; /// Style name - boost::flyweight Style; + boost::flyweight Style; /// Actor name - boost::flyweight Actor; + boost::flyweight Actor; /// Effect name - boost::flyweight Effect; + boost::flyweight Effect; /// Raw text data - boost::flyweight Text; + boost::flyweight Text; AssEntryGroup Group() const override { return ENTRY_DIALOGUE; } - /// @brief Parse raw ASS data into everything else - /// @param data ASS line - /// @return Did it successfully parse? - bool Parse(wxString const& data); - /// Parse text as ASS and return block information std::auto_ptr> ParseTags() const; @@ -166,23 +163,23 @@ public: void StripTags(); /// Strip a specific ASS tag from the text /// Get text without tags - wxString GetStrippedText() const; + std::string GetStrippedText() const; /// Update the text of the line from parsed blocks void UpdateText(boost::ptr_vector& blocks); - const wxString GetEntryData() const override; + const std::string GetEntryData() const override; template - void SetMarginString(wxString const& value) { SetMarginString(value, which);} + void SetMarginString(std::string const& value) { SetMarginString(value, which);} /// @brief Set a margin /// @param value New value of the margin /// @param which 0 = left, 1 = right, 2 = vertical - void SetMarginString(wxString const& value, int which); + void SetMarginString(std::string const& value, int which); /// @brief Get a margin /// @param which 0 = left, 1 = right, 2 = vertical - wxString GetMarginString(int which) const; + std::string GetMarginString(int which) const; /// Get the line as SSA rather than ASS - wxString GetSSAText() const override; + std::string GetSSAText() const override; /// Does this line collide with the passed line? bool CollidesWith(const AssDialogue *target) const; @@ -190,7 +187,7 @@ public: AssDialogue(); AssDialogue(AssDialogue const&); - AssDialogue(wxString const& data); + AssDialogue(std::string const& data); ~AssDialogue(); }; diff --git a/aegisub/src/ass_entry.cpp b/aegisub/src/ass_entry.cpp index fd3f6f334..913f6e8bc 100644 --- a/aegisub/src/ass_entry.cpp +++ b/aegisub/src/ass_entry.cpp @@ -23,8 +23,8 @@ #include "ass_entry.h" -wxString const& AssEntry::GroupHeader(bool ssa) const { - static wxString ass_headers[] = { +std::string const& AssEntry::GroupHeader(bool ssa) const { + static std::string ass_headers[] = { "[Script Info]", "[V4+ Styles]", "[Fonts]", @@ -33,7 +33,7 @@ wxString const& AssEntry::GroupHeader(bool ssa) const { "" }; - static wxString ssa_headers[] = { + static std::string ssa_headers[] = { "[Script Info]", "[V4 Styles]", "[Fonts]", diff --git a/aegisub/src/ass_entry.h b/aegisub/src/ass_entry.h index deff83e0a..1871a6271 100644 --- a/aegisub/src/ass_entry.h +++ b/aegisub/src/ass_entry.h @@ -34,9 +34,8 @@ #pragma once -#include - #include +#include enum AssEntryGroup { ENTRY_INFO = 0, @@ -58,11 +57,11 @@ public: virtual AssEntryGroup Group() const=0; /// ASS or SSA Section header for this entry's group - wxString const& GroupHeader(bool ssa=false) const; + std::string const& GroupHeader(bool ssa=false) const; /// @brief Get this line's raw entry data in ASS format - virtual const wxString GetEntryData() const=0; + virtual const std::string GetEntryData() const=0; /// Get this line in SSA format - virtual wxString GetSSAText() const { return GetEntryData(); } + virtual std::string GetSSAText() const { return GetEntryData(); } }; diff --git a/aegisub/src/ass_export_filter.cpp b/aegisub/src/ass_export_filter.cpp index 98716b01c..c0147a885 100644 --- a/aegisub/src/ass_export_filter.cpp +++ b/aegisub/src/ass_export_filter.cpp @@ -34,12 +34,19 @@ #include "config.h" -#include - #include "ass_export_filter.h" + #include "utils.h" -AssExportFilter::AssExportFilter(wxString const& name, wxString const& description, int priority) +#include +#include + +static FilterList& filters() { + static FilterList instance; + return instance; +} + +AssExportFilter::AssExportFilter(std::string const& name, std::string const& description, int priority) : name(name) , priority(priority) , description(description) @@ -47,52 +54,44 @@ AssExportFilter::AssExportFilter(wxString const& name, wxString const& descripti } void AssExportFilterChain::Register(AssExportFilter *filter) { - // Remove pipes from name - filter->name.Replace("|", ""); - int filter_copy = 1; - wxString name = filter->name; + std::string name = filter->name; // Find a unique name while (GetFilter(name)) - name = wxString::Format("%s (%d)", filter->name, filter_copy++); + name = str(boost::format("%s (%d)") % filter->name % filter_copy++); filter->name = name; // Look for place to insert - FilterList::iterator begin = filters()->begin(); - FilterList::iterator end = filters()->end(); + auto begin(filters().begin()), end(filters().end()); while (begin != end && (*begin)->priority >= filter->priority) ++begin; - filters()->insert(begin, filter); + filters().insert(begin, filter); } void AssExportFilterChain::Unregister(AssExportFilter *filter) { - auto it = remove(begin(*filters()), end(*filters()), filter); - if (it == end(*filters())) + auto it = remove(begin(filters()), end(filters()), filter); + if (it == end(filters())) throw wxString::Format("Unregister export filter: name \"%s\" is not registered.", filter->name); - filters()->pop_back(); + filters().pop_back(); } -FilterList *AssExportFilterChain::filters() { - static FilterList instance; - return &instance; -} const FilterList *AssExportFilterChain::GetFilterList() { - return filters(); + return &filters(); } void AssExportFilterChain::Clear() { - while (filters()->size() > 0) { - AssExportFilter *f = filters()->back(); + while (filters().size() > 0) { + AssExportFilter *f = filters().back(); delete f; - if (filters()->size() && filters()->back() == f) - filters()->pop_back(); + if (filters().size() && filters().back() == f) + filters().pop_back(); } } -AssExportFilter *AssExportFilterChain::GetFilter(wxString const& name) { - for (auto filter : *filters()) { +AssExportFilter *AssExportFilterChain::GetFilter(std::string const& name) { + for (auto filter : filters()) { if (filter->name == name) return filter; } diff --git a/aegisub/src/ass_export_filter.h b/aegisub/src/ass_export_filter.h index a36744462..0483adb98 100644 --- a/aegisub/src/ass_export_filter.h +++ b/aegisub/src/ass_export_filter.h @@ -35,20 +35,18 @@ #pragma once #include +#include #include -#include -#include - class AssFile; class AssExportFilter; +class wxWindow; namespace agi { struct Context; } typedef std::vector FilterList; class AssExportFilterChain { - static FilterList *filters(); public: /// Register an export filter static void Register(AssExportFilter *filter); @@ -57,7 +55,7 @@ public: /// Unregister and delete all export filters static void Clear(); /// Get a filter by name or nullptr if it doesn't exist - static AssExportFilter *GetFilter(wxString const& name); + static AssExportFilter *GetFilter(std::string const& name); /// Get the list of registered filters static const FilterList *GetFilterList(); @@ -69,20 +67,20 @@ class AssExportFilter { friend class AssExportFilterChain; /// This filter's name - wxString name; + std::string name; /// Higher priority = run earlier int priority; /// User-visible description of this filter - wxString description; + std::string description; public: - AssExportFilter(wxString const& name, wxString const& description, int priority = 0); + AssExportFilter(std::string const& name, std::string const& description, int priority = 0); virtual ~AssExportFilter() { }; - wxString const& GetName() const { return name; } - wxString const& GetDescription() const { return description; } + std::string const& GetName() const { return name; } + std::string const& GetDescription() const { return description; } /// Process subtitles /// @param subs Subtitles to process diff --git a/aegisub/src/ass_exporter.cpp b/aegisub/src/ass_exporter.cpp index de2e277ed..894e4ab42 100644 --- a/aegisub/src/ass_exporter.cpp +++ b/aegisub/src/ass_exporter.cpp @@ -34,14 +34,16 @@ #include "config.h" -#include "ass_export_filter.h" #include "ass_exporter.h" + +#include "ass_export_filter.h" #include "ass_file.h" +#include "compat.h" #include "include/aegisub/context.h" -#include - #include +#include +#include static inline FilterList::const_iterator filter_list_begin() { return AssExportFilterChain::GetFilterList()->begin(); @@ -62,7 +64,7 @@ void AssExporter::DrawSettings(wxWindow *parent, wxSizer *target_sizer) { for (auto filter : *AssExportFilterChain::GetFilterList()) { // Make sure to construct static box sizer first, so it won't overlap // the controls on wxMac. - wxSizer *box = new wxStaticBoxSizer(wxVERTICAL, parent, filter->GetName()); + wxSizer *box = new wxStaticBoxSizer(wxVERTICAL, parent, to_wx(filter->GetName())); wxWindow *window = filter->GetConfigDialogWindow(parent, c); if (window) { box->Add(window, 0, wxEXPAND, 0); @@ -76,18 +78,18 @@ void AssExporter::DrawSettings(wxWindow *parent, wxSizer *target_sizer) { } } -void AssExporter::AddFilter(wxString const& name) { +void AssExporter::AddFilter(std::string const& name) { AssExportFilter *filter = AssExportFilterChain::GetFilter(name); - if (!filter) throw wxString::Format("Filter not found: %s", name); + if (!filter) throw "Filter not found: " + name; filters.push_back(filter); } -wxArrayString AssExporter::GetAllFilterNames() const { - wxArrayString names; +std::vector AssExporter::GetAllFilterNames() const { + std::vector names; transform(filter_list_begin(), filter_list_end(), - std::back_inserter(names), std::mem_fun(&AssExportFilter::GetName)); + back_inserter(names), std::mem_fun(&AssExportFilter::GetName)); return names; } @@ -102,19 +104,19 @@ AssFile *AssExporter::ExportTransform(wxWindow *export_dialog, bool copy) { return subs; } -void AssExporter::Export(wxString const& filename, wxString const& charset, wxWindow *export_dialog) { - agi::scoped_ptr subs(ExportTransform(export_dialog, true)); +void AssExporter::Export(agi::fs::path const& filename, std::string const& charset, wxWindow *export_dialog) { + std::unique_ptr subs(ExportTransform(export_dialog, true)); subs->Save(filename, false, false, charset); } -wxSizer *AssExporter::GetSettingsSizer(wxString const& name) { +wxSizer *AssExporter::GetSettingsSizer(std::string const& name) { auto pos = Sizers.find(name); return pos == Sizers.end() ? nullptr : pos->second; } -wxString const& AssExporter::GetDescription(wxString const& name) const { +std::string const& AssExporter::GetDescription(std::string const& name) const { AssExportFilter *filter = AssExportFilterChain::GetFilter(name); if (filter) return filter->GetDescription(); - throw wxString::Format("Filter not found: %s", name); + throw "Filter not found: " + name; } diff --git a/aegisub/src/ass_exporter.h b/aegisub/src/ass_exporter.h index 86764e5d2..0b9671d14 100644 --- a/aegisub/src/ass_exporter.h +++ b/aegisub/src/ass_exporter.h @@ -32,16 +32,17 @@ /// @ingroup export /// -#include -#include -#include +#include #include +#include #include class AssExportFilter; class AssFile; namespace agi { struct Context; } +class wxSizer; +class wxWindow; typedef std::vector FilterList; @@ -49,7 +50,7 @@ class AssExporter { typedef FilterList::const_iterator filter_iterator; /// Sizers for configuration panels - std::map Sizers; + std::map Sizers; /// Filters which will be applied to the subtitles FilterList filters; @@ -65,11 +66,11 @@ public: AssExporter(agi::Context *c); /// Get the names of all registered export filters - wxArrayString GetAllFilterNames() const; + std::vector GetAllFilterNames() const; /// Add the named filter to the list of filters to be run - /// @throws wxString if filter is not found - void AddFilter(wxString const& name); + /// @throws std::string if filter is not found + void AddFilter(std::string const& name); /// Run all added export filters /// @param parent_window Parent window the filters should use when opening dialogs @@ -81,7 +82,7 @@ public: /// @param file Target filename /// @param charset Target charset /// @param parent_window Parent window the filters should use when opening dialogs - void Export(wxString const& file, wxString const& charset, wxWindow *parent_window= 0); + void Export(agi::fs::path const& file, std::string const& charset, wxWindow *parent_window= 0); /// Add configuration panels for all registered filters to the target sizer /// @param parent Parent window for controls @@ -89,9 +90,9 @@ public: void DrawSettings(wxWindow *parent, wxSizer *target_sizer); /// Get the sizer created by DrawSettings for a specific filter - wxSizer *GetSettingsSizer(wxString const& name); + wxSizer *GetSettingsSizer(std::string const& name); /// Get the description of the named export filter - /// @throws wxString if filter is not found - wxString const& GetDescription(wxString const& name) const; + /// @throws std::string if filter is not found + std::string const& GetDescription(std::string const& name) const; }; diff --git a/aegisub/src/ass_file.cpp b/aegisub/src/ass_file.cpp index 4bdc90ffe..75974a67b 100644 --- a/aegisub/src/ass_file.cpp +++ b/aegisub/src/ass_file.cpp @@ -35,21 +35,10 @@ #include "ass_file.h" -#include -#include -#include -#include -#include - -#include -#include -#include - #include "ass_attachment.h" #include "ass_dialogue.h" #include "ass_info.h" #include "ass_style.h" -#include "compat.h" #include "options.h" #include "standard_paths.h" #include "subtitle_format.h" @@ -57,7 +46,17 @@ #include "text_file_writer.h" #include "utils.h" +#include +#include #include +#include + +#include +#include +#include +#include +#include +#include namespace std { template<> @@ -73,16 +72,18 @@ AssFile::AssFile () } AssFile::~AssFile() { - background_delete_clear(Line); + auto copy = new EntryList; + copy->swap(Line); + agi::dispatch::Background().Async([=]{ delete copy; }); } /// @brief Load generic subs -void AssFile::Load(const wxString &_filename, wxString const& charset) { - const SubtitleFormat *reader = SubtitleFormat::GetReader(_filename); +void AssFile::Load(agi::fs::path const& filename, std::string const& charset) { + const SubtitleFormat *reader = SubtitleFormat::GetReader(filename); try { AssFile temp; - reader->ReadFile(&temp, _filename, charset); + reader->ReadFile(&temp, filename, charset); bool found_style = false; bool found_dialogue = false; @@ -109,7 +110,7 @@ void AssFile::Load(const wxString &_filename, wxString const& charset) { // Set general data loaded = true; - filename = _filename; + this->filename = filename; // Add comments and set vars SetScriptInfo("ScriptType", "v4.00+"); @@ -124,7 +125,7 @@ void AssFile::Load(const wxString &_filename, wxString const& charset) { FileOpen(filename); } -void AssFile::Save(wxString filename, bool setfilename, bool addToRecent, wxString encoding) { +void AssFile::Save(agi::fs::path const& filename, bool setfilename, bool addToRecent, std::string const& encoding) { const SubtitleFormat *writer = SubtitleFormat::GetWriter(filename); if (!writer) throw "Unknown file type."; @@ -132,47 +133,38 @@ void AssFile::Save(wxString filename, bool setfilename, bool addToRecent, wxStri if (setfilename) { autosavedCommitId = savedCommitId = commitId; this->filename = filename; - StandardPaths::SetPathValue("?script", wxFileName(filename).GetPath()); + StandardPaths::SetPathValue("?script", filename.parent_path()); } FileSave(); writer->WriteFile(this, filename, encoding); - if (addToRecent) { + if (addToRecent) AddToRecent(filename); - } } -wxString AssFile::AutoSave() { +agi::fs::path AssFile::AutoSave() { if (!loaded || commitId == autosavedCommitId) return ""; - wxFileName origfile(filename); - wxString path = to_wx(OPT_GET("Path/Auto/Save")->GetString()); - if (!path) - path = origfile.GetPath(); - path = StandardPaths::DecodePath(path + "/"); + auto path = StandardPaths::DecodePath(OPT_GET("Path/Auto/Save")->GetString()); + if (path.empty()) + path = filename.parent_path(); - wxFileName dstpath(path); - if (!dstpath.DirExists()) - wxMkdir(path); + agi::fs::CreateDirectory(path); - wxString name = origfile.GetName(); - if (!name) + auto name = filename.filename(); + if (name.empty()) name = "Untitled"; - dstpath.SetFullName(wxString::Format("%s.%s.AUTOSAVE.ass", name, wxDateTime::Now().Format("%Y-%m-%d-%H-%M-%S"))); - Save(dstpath.GetFullPath(), false, false); + path /= str(boost::format("%s.%s.AUTOSAVE.ass") % name % agi::util::strftime("%Y-%m-%d-%H-%M-%S")); + + Save(path, false, false); autosavedCommitId = commitId; - return dstpath.GetFullPath(); -} - -static void write_line(wxString const& line, std::vector& dst) { - wxCharBuffer buffer = (line + "\r\n").utf8_str(); - copy(buffer.data(), buffer.data() + buffer.length(), back_inserter(dst)); + return path; } void AssFile::SaveMemory(std::vector &dst) { @@ -190,9 +182,9 @@ void AssFile::SaveMemory(std::vector &dst) { for (auto const& line : Line) { if (group != line.Group()) { group = line.Group(); - write_line(line.GroupHeader(), dst); + boost::push_back(dst, line.GroupHeader() + "\r\n"); } - write_line(line.GetEntryData(), dst); + boost::push_back(dst, line.GetEntryData() + "\r\n"); } } @@ -205,28 +197,15 @@ bool AssFile::CanSave() const { } } -void AssFile::Clear() { - background_delete_clear(Line); - - loaded = false; - filename.clear(); - UndoStack.clear(); - RedoStack.clear(); - undoDescription.clear(); -} - void AssFile::LoadDefault(bool defline) { - Clear(); - - // Write headers Line.push_back(*new AssInfo("Title", "Default Aegisub file")); Line.push_back(*new AssInfo("ScriptType", "v4.00+")); Line.push_back(*new AssInfo("WrapStyle", "0")); Line.push_back(*new AssInfo("ScaledBorderAndShadow", "yes")); Line.push_back(*new AssInfo("Collisions", "Normal")); if (!OPT_GET("Subtitle/Default Resolution/Auto")->GetBool()) { - Line.push_back(*new AssInfo("PlayResX", wxString::Format("%" PRId64, OPT_GET("Subtitle/Default Resolution/Width")->GetInt()))); - Line.push_back(*new AssInfo("PlayResY", wxString::Format("%" PRId64, OPT_GET("Subtitle/Default Resolution/Height")->GetInt()))); + Line.push_back(*new AssInfo("PlayResX", std::to_string(OPT_GET("Subtitle/Default Resolution/Width")->GetInt()))); + Line.push_back(*new AssInfo("PlayResY", std::to_string(OPT_GET("Subtitle/Default Resolution/Height")->GetInt()))); } Line.push_back(*new AssInfo("YCbCr Matrix", "None")); @@ -283,40 +262,32 @@ void AssFile::InsertLine(AssEntry *entry) { Line.push_front(*entry); } -void AssFile::InsertAttachment(wxString filename) { +void AssFile::InsertAttachment(agi::fs::path const& filename) { AssEntryGroup group = ENTRY_GRAPHIC; - wxString ext = filename.Right(4).Lower(); + auto ext = boost::to_lower_copy(filename.extension().string()); if (ext == ".ttf" || ext == ".ttc" || ext == ".pfb") group = ENTRY_FONT; - std::unique_ptr newAttach(new AssAttachment(wxFileName(filename).GetFullName(), group)); - newAttach->Import(filename); - - InsertLine(newAttach.release()); + InsertLine(new AssAttachment(filename, group)); } -wxString AssFile::GetScriptInfo(wxString key) const { - key.MakeLower(); - +std::string AssFile::GetScriptInfo(std::string const& key) const { for (const auto info : Line | agi::of_type()) { - if (key == info->Key().Lower()) + if (boost::iequals(key, info->Key())) return info->Value(); } return ""; } -int AssFile::GetScriptInfoAsInt(wxString const& key) const { - long temp = 0; - GetScriptInfo(key).ToLong(&temp); - return temp; +int AssFile::GetScriptInfoAsInt(std::string const& key) const { + return atoi(GetScriptInfo(key).c_str()); } -void AssFile::SetScriptInfo(wxString const& key, wxString const& value) { - wxString lower_key = key.Lower(); +void AssFile::SetScriptInfo(std::string const& key, std::string const& value) { for (auto info : Line | agi::of_type()) { - if (lower_key == info->Key().Lower()) { + if (boost::iequals(key, info->Key())) { if (value.empty()) delete info; else @@ -366,10 +337,9 @@ AssStyle *AssFile::GetStyle(std::string const& name) { return nullptr; } -void AssFile::AddToRecent(wxString const& file) const { - config::mru->Add("Subtitle", from_wx(file)); - wxFileName filepath(file); - OPT_SET("Path/Last/Subtitles")->SetString(from_wx(filepath.GetPath())); +void AssFile::AddToRecent(agi::fs::path const& file) const { + config::mru->Add("Subtitle", file); + OPT_SET("Path/Last/Subtitles")->SetString(file.parent_path().string()); } int AssFile::Commit(wxString const& desc, int type, int amendId, AssEntry *single_line) { diff --git a/aegisub/src/ass_file.h b/aegisub/src/ass_file.h index c56528122..3fcba72d0 100644 --- a/aegisub/src/ass_file.h +++ b/aegisub/src/ass_file.h @@ -33,10 +33,13 @@ /// #include +#include #include #include #include +#include +#include #include #include "ass_entry.h" @@ -63,7 +66,7 @@ class AssFile { /// A set of changes has been committed to the file (AssFile::CommitType) agi::signal::Signal const&> AnnounceCommit; /// A new file has been opened (filename) - agi::signal::Signal FileOpen; + agi::signal::Signal FileOpen; /// The file is about to be saved /// This signal is intended for adding metadata such as video filename, /// frame number, etc. Ideally this would all be done immediately rather @@ -74,7 +77,7 @@ public: /// The lines in the file EntryList Line; /// The filename of this file, if any - wxString filename; + agi::fs::path filename; /// Is the file loaded? bool loaded; @@ -84,9 +87,7 @@ public: ~AssFile(); /// Does the file have unsaved changes? - bool IsModified() const {return commitId != savedCommitId; }; - /// Clear the file - void Clear(); + bool IsModified() const { return commitId != savedCommitId; }; /// @brief Load default file /// @param defline Add a blank line to the file @@ -94,7 +95,7 @@ public: /// Add a line to the file at the end of the appropriate section void InsertLine(AssEntry *line); /// Attach a file to the ass file - void InsertAttachment(wxString filename); + void InsertAttachment(agi::fs::path const& filename); /// Get the names of all of the styles available std::vector GetStyles() const; /// @brief Get a style by name @@ -107,24 +108,24 @@ public: /// @brief Load from a file /// @param file File name /// @param charset Character set of file or empty to autodetect - void Load(const wxString &file, wxString const& charset=""); + void Load(agi::fs::path const& file, std::string const& charset=""); /// @brief Save to a file /// @param file Path to save to /// @param setfilename Should the filename be changed to the passed path? /// @param addToRecent Should the file be added to the MRU list? /// @param encoding Encoding to use, or empty to let the writer decide (which usually means "App/Save Charset") - void Save(wxString file,bool setfilename=false,bool addToRecent=true,const wxString encoding=""); + void Save(agi::fs::path const& file, bool setfilename=false, bool addToRecent=true, std::string const& encoding=""); /// @brief Autosave the file if there have been any chances since the last autosave /// @return File name used or empty if no save was performed - wxString AutoSave(); + agi::fs::path AutoSave(); /// @brief Save to a memory buffer. Used for subtitle providers which support it /// @param[out] dst Destination vector void SaveMemory(std::vector &dst); /// Add file name to the MRU list - void AddToRecent(wxString const& file) const; + void AddToRecent(agi::fs::path const& file) const; /// Can the file be saved in its current format? bool CanSave() const; @@ -133,11 +134,11 @@ public: /// @param[in] h Height void GetResolution(int &w,int &h) const; /// Get the value in a [Script Info] key as int, or 0 if it is not present - int GetScriptInfoAsInt(wxString const& key) const; + int GetScriptInfoAsInt(std::string const& key) const; /// Get the value in a [Script Info] key as string. - wxString GetScriptInfo(wxString key) const; + std::string GetScriptInfo(std::string const& key) const; /// Set the value of a [Script Info] key. Adds it if it doesn't exist. - void SetScriptInfo(wxString const& key, wxString const& value); + void SetScriptInfo(std::string const& key, std::string const& value); /// Type of changes made in a commit enum CommitType { diff --git a/aegisub/src/ass_info.h b/aegisub/src/ass_info.h index ff80d9f27..7b49c314c 100644 --- a/aegisub/src/ass_info.h +++ b/aegisub/src/ass_info.h @@ -16,21 +16,22 @@ #include "ass_entry.h" +#include + class AssInfo : public AssEntry { - wxString key; - wxString value; + std::string key; + std::string value; public: AssInfo(AssInfo const& o) : key(o.key), value(o.value) { } - AssInfo(wxString const& line) : key(line.BeforeFirst(':').Trim()), value(line.AfterFirst(':').Trim(false)) { } - AssInfo(wxString const& key, wxString const& value) : key(key), value(value) { } + AssInfo(std::string const& key, std::string const& value) : key(key), value(value) { } AssEntry *Clone() const override { return new AssInfo(*this); } AssEntryGroup Group() const override { return ENTRY_INFO; } - const wxString GetEntryData() const override { return key + ": " + value; } - wxString GetSSAText() const override { return key.Lower() == "scripttype: v4.00+" ? "ScriptType: v4.00" : GetEntryData(); } + const std::string GetEntryData() const override { return key + ": " + value; } + std::string GetSSAText() const override { return boost::iequals(key, "scripttype: v4.00+") ? "ScriptType: v4.00" : GetEntryData(); } - wxString Key() const { return key; } - wxString Value() const { return value; } - void SetValue(wxString const& new_value) { value = new_value; } + std::string Key() const { return key; } + std::string Value() const { return value; } + void SetValue(std::string const& new_value) { value = new_value; } }; diff --git a/aegisub/src/ass_karaoke.cpp b/aegisub/src/ass_karaoke.cpp index 0f34fed15..a199dce53 100644 --- a/aegisub/src/ass_karaoke.cpp +++ b/aegisub/src/ass_karaoke.cpp @@ -25,7 +25,6 @@ #include "ass_dialogue.h" #include "ass_file.h" -#include "compat.h" #include "include/aegisub/context.h" #include "selection_controller.h" @@ -161,12 +160,12 @@ void AssKaraoke::ParseSyllables(AssDialogue *line, Syllable &syl) { syls.push_back(syl); } -wxString AssKaraoke::GetText() const { - wxString text; +std::string AssKaraoke::GetText() const { + std::string text; text.reserve(size() * 10); for (auto const& syl : syls) - text += to_wx(syl.GetText(true)); + text += syl.GetText(true); return text; } @@ -301,7 +300,7 @@ void AssKaraoke::SplitLines(std::set const& lines, agi::Context *c new_line->Start = syl.start_time; new_line->End = syl.start_time + syl.duration; - new_line->Text = to_wx(syl.GetText(false)); + new_line->Text = syl.GetText(false); c->ass->Line.insert(it, *new_line); diff --git a/aegisub/src/ass_karaoke.h b/aegisub/src/ass_karaoke.h index 2c200327f..95275053b 100644 --- a/aegisub/src/ass_karaoke.h +++ b/aegisub/src/ass_karaoke.h @@ -25,8 +25,6 @@ #include #include -#include - #include namespace agi { struct Context; } @@ -84,7 +82,7 @@ public: size_t size() const { return syls.size(); } /// Get the line's text with k tags - wxString GetText() const; + std::string GetText() const; /// Get the karaoke tag type used, with leading slash /// @returns "\k", "\kf", or "\ko" diff --git a/aegisub/src/ass_parser.cpp b/aegisub/src/ass_parser.cpp index 5bbc266f0..84755b101 100644 --- a/aegisub/src/ass_parser.cpp +++ b/aegisub/src/ass_parser.cpp @@ -24,8 +24,9 @@ #include "subtitle_format.h" #include - -#include +#include +#include +#include AssParser::AssParser(AssFile *target, int version) : target(target) @@ -39,8 +40,8 @@ AssParser::AssParser(AssFile *target, int version) AssParser::~AssParser() { } -void AssParser::ParseAttachmentLine(wxString const& data) { - bool is_filename = data.StartsWith("fontname: ") || data.StartsWith("filename: "); +void AssParser::ParseAttachmentLine(std::string const& data) { + bool is_filename = boost::starts_with(data, "fontname: ") || boost::starts_with(data, "filename: "); bool valid_data = data.size() > 0 && data.size() <= 80; for (auto byte : data) { @@ -60,59 +61,59 @@ void AssParser::ParseAttachmentLine(wxString const& data) { attach->AddData(data); // Done building - if (data.Length() < 80) { + if (data.size() < 80) { attach->Finish(); InsertLine(attach.release()); } } } -void AssParser::ParseScriptInfoLine(wxString const& data) { - if (data.StartsWith(";")) { +void AssParser::ParseScriptInfoLine(std::string const& data) { + if (boost::starts_with(data, ";")) { // Skip stupid comments added by other programs // Of course, we'll add our own in place later... ;) return; } - if (data.StartsWith("ScriptType:")) { - wxString versionString = data.Mid(11).Trim(true).Trim(false).Lower(); - int trueVersion; - if (versionString == "v4.00") - trueVersion = 0; - else if (versionString == "v4.00+") - trueVersion = 1; + if (boost::starts_with(data, "ScriptType:")) { + std::string version_str = data.substr(11); + boost::trim(version_str); + boost::to_lower(version_str); + if (version_str == "v4.00") + version = 0; + else if (version_str == "v4.00+") + version = 1; else throw SubtitleFormatParseError("Unknown SSA file format version", 0); - if (trueVersion != version) { - wxLogMessage("Warning: File has the wrong extension."); - version = trueVersion; - } } - InsertLine(new AssInfo(data)); + size_t pos = data.find(':'); + if (pos == data.npos) return; + + InsertLine(new AssInfo(data.substr(0, pos), boost::trim_left_copy(data.substr(pos + 1)))); } -void AssParser::ParseEventLine(wxString const& data) { - if (data.StartsWith("Dialogue:") || data.StartsWith("Comment:")) +void AssParser::ParseEventLine(std::string const& data) { + if (boost::starts_with(data, "Dialogue:") || boost::starts_with(data, "Comment:")) InsertLine(new AssDialogue(data)); } -void AssParser::ParseStyleLine(wxString const& data) { - if (data.StartsWith("Style:")) +void AssParser::ParseStyleLine(std::string const& data) { + if (boost::starts_with(data, "Style:")) InsertLine(new AssStyle(data, version)); } -void AssParser::ParseFontLine(wxString const& data) { - if (data.StartsWith("fontname: ")) - attach.reset(new AssAttachment(data.Mid(10), ENTRY_FONT)); +void AssParser::ParseFontLine(std::string const& data) { + if (boost::starts_with(data, "fontname: ")) + attach.reset(new AssAttachment(data.substr(10), ENTRY_FONT)); } -void AssParser::ParseGraphicsLine(wxString const& data) { - if (data.StartsWith("filename: ")) - attach.reset(new AssAttachment(data.Mid(10), ENTRY_GRAPHIC)); +void AssParser::ParseGraphicsLine(std::string const& data) { + if (boost::starts_with(data, "filename: ")) + attach.reset(new AssAttachment(data.substr(10), ENTRY_GRAPHIC)); } -void AssParser::AddLine(wxString const& data) { +void AssParser::AddLine(std::string const& data) { // Special-case for attachments since a line could theoretically be both a // valid attachment data line and a valid section header, and if an // attachment is in progress it needs to be treated as that @@ -124,10 +125,10 @@ void AssParser::AddLine(wxString const& data) { if (data.empty()) return; // Section header - if (data[0] == '[' && data.Last() == ']') { + if (data[0] == '[' && data.back() == ']') { // Ugly hacks to allow intermixed v4 and v4+ style sections - const wxString low = data.Lower(); - wxString header = data; + const std::string low = boost::to_lower_copy(data); + std::string header = data; if (low == "[v4 styles]") { header = "[V4+ Styles]"; version = 0; diff --git a/aegisub/src/ass_parser.h b/aegisub/src/ass_parser.h index eabb2dbd6..fb0a813ae 100644 --- a/aegisub/src/ass_parser.h +++ b/aegisub/src/ass_parser.h @@ -16,8 +16,6 @@ #include #include -#include - #include "ass_entry.h" class AssAttachment; @@ -27,20 +25,21 @@ class AssParser { AssFile *target; int version; std::unique_ptr attach; - void (AssParser::*state)(wxString const&); + void (AssParser::*state)(std::string const&); std::array insertion_positions; void InsertLine(AssEntry *entry); - void ParseAttachmentLine(wxString const& data); - void ParseEventLine(wxString const& data); - void ParseStyleLine(wxString const& data); - void ParseScriptInfoLine(wxString const& data); - void ParseFontLine(wxString const& data); - void ParseGraphicsLine(wxString const& data); - void UnknownLine(wxString const&) { } + void ParseAttachmentLine(std::string const& data); + void ParseEventLine(std::string const& data); + void ParseStyleLine(std::string const& data); + void ParseScriptInfoLine(std::string const& data); + void ParseFontLine(std::string const& data); + void ParseGraphicsLine(std::string const& data); + void UnknownLine(std::string const&) { } public: AssParser(AssFile *target, int version); ~AssParser(); - void AddLine(wxString const& data); + + void AddLine(std::string const& data); }; diff --git a/aegisub/src/ass_style.cpp b/aegisub/src/ass_style.cpp index 085107c48..843a86487 100644 --- a/aegisub/src/ass_style.cpp +++ b/aegisub/src/ass_style.cpp @@ -35,13 +35,15 @@ #include "config.h" #include "ass_style.h" -#include "compat.h" + #include "subtitle_format.h" #include "utils.h" +#include + #include -#include #include +#include #include #include @@ -74,42 +76,28 @@ AssStyle::AssStyle() namespace { class parser { - typedef boost::iterator_range string_range; - string_range str; - std::vector tkns; - size_t tkn_idx; + boost::split_iterator pos; - string_range next_tok() { - if (tkn_idx >= tkns.size()) + std::string next_tok() { + if (pos.eof()) throw SubtitleFormatParseError("Malformed style: not enough fields", 0); - return trim_copy(tkns[tkn_idx++]); + return agi::str(trim_copy(*pos++)); } public: - parser(std::string const& str) - : tkn_idx(0) - { - auto pos = find(str.begin(), str.end(), ':'); - if (pos != str.end()) { - this->str = string_range(pos + 1, str.end()); - split(tkns, this->str, [](char c) { return c == ','; }); - } + parser(std::string const& str) { + auto colon = find(str.begin(), str.end(), ':'); + if (colon != str.end()) + pos = agi::Split(agi::StringRange(colon + 1, str.end()), ','); } void check_done() const { - if (tkn_idx != tkns.size()) + if (!pos.eof()) throw SubtitleFormatParseError("Malformed style: too many fields", 0); } - std::string next_str() { - auto tkn = next_tok(); - return std::string(begin(tkn), end(tkn)); - } - - agi::Color next_color() { - auto tkn = next_tok(); - return std::string(begin(tkn), end(tkn)); - } + std::string next_str() { return next_tok(); } + agi::Color next_color() { return next_tok(); } int next_int() { try { @@ -130,13 +118,13 @@ public: } void skip_token() { - ++tkn_idx; + if (!pos.eof()) + ++pos; } }; } -AssStyle::AssStyle(wxString const& rawData, int version) { - std::string str(from_wx(rawData)); +AssStyle::AssStyle(std::string const& str, int version) { parser p(str); name = p.next_str(); @@ -211,28 +199,28 @@ void AssStyle::UpdateData() { replace(name.begin(), name.end(), ',', ';'); replace(font.begin(), font.end(), ',', ';'); - data = wxString::Format("Style: %s,%s,%g,%s,%s,%s,%s,%d,%d,%d,%d,%g,%g,%g,%g,%d,%g,%g,%i,%i,%i,%i,%i", - to_wx(name), to_wx(font), fontsize, - primary.GetAssStyleFormatted(), - secondary.GetAssStyleFormatted(), - outline.GetAssStyleFormatted(), - shadow.GetAssStyleFormatted(), - (bold? -1 : 0), (italic ? -1 : 0), - (underline?-1:0),(strikeout?-1:0), - scalex,scaley,spacing,angle, - borderstyle,outline_w,shadow_w,alignment, - Margin[0],Margin[1],Margin[2],encoding); + data = str(boost::format("Style: %s,%s,%g,%s,%s,%s,%s,%d,%d,%d,%d,%g,%g,%g,%g,%d,%g,%g,%i,%i,%i,%i,%i") + % name % font % fontsize + % primary.GetAssStyleFormatted() + % secondary.GetAssStyleFormatted() + % outline.GetAssStyleFormatted() + % shadow.GetAssStyleFormatted() + % (bold? -1 : 0) % (italic ? -1 : 0) + % (underline ? -1 : 0) % (strikeout ? -1 : 0) + % scalex % scaley % spacing % angle + % borderstyle % outline_w % shadow_w % alignment + % Margin[0] % Margin[1] % Margin[2] % encoding); } -wxString AssStyle::GetSSAText() const { - return wxString::Format("Style: %s,%s,%g,%s,%s,0,%s,%d,%d,%d,%g,%g,%d,%d,%d,%d,0,%i", - to_wx(name), to_wx(font), fontsize, - primary.GetSsaFormatted(), - secondary.GetSsaFormatted(), - shadow.GetSsaFormatted(), - (bold? -1 : 0), (italic ? -1 : 0), - borderstyle,outline_w,shadow_w,AssToSsa(alignment), - Margin[0],Margin[1],Margin[2],encoding); +std::string AssStyle::GetSSAText() const { + return str(boost::format("Style: %s,%s,%g,%s,%s,0,%s,%d,%d,%d,%g,%g,%d,%d,%d,%d,0,%i") + % name % font % fontsize + % primary.GetSsaFormatted() + % secondary.GetSsaFormatted() + % shadow.GetSsaFormatted() + % (bold? -1 : 0) % (italic ? -1 : 0) + % borderstyle % outline_w % shadow_w % AssToSsa(alignment) + % Margin[0] % Margin[1] % Margin[2] % encoding); } AssEntry *AssStyle::Clone() const { diff --git a/aegisub/src/ass_style.h b/aegisub/src/ass_style.h index 2c3e9a8db..bd2c3ded7 100644 --- a/aegisub/src/ass_style.h +++ b/aegisub/src/ass_style.h @@ -40,7 +40,7 @@ #include class AssStyle : public AssEntry { - wxString data; + std::string data; public: std::string name; ///< Name of the style; must be case-insensitively unique within a file despite being case-sensitive @@ -75,10 +75,10 @@ public: static void GetEncodings(wxArrayString &encodingStrings); AssStyle(); - AssStyle(wxString const& data, int version=1); + AssStyle(std::string const& data, int version=1); - const wxString GetEntryData() const override { return data; } - wxString GetSSAText() const override; + const std::string GetEntryData() const override { return data; } + std::string GetSSAText() const override; AssEntryGroup Group() const override { return ENTRY_STYLE; } AssEntry *Clone() const override; diff --git a/aegisub/src/ass_style_storage.cpp b/aegisub/src/ass_style_storage.cpp index ac0da6853..f3c4f8ef9 100644 --- a/aegisub/src/ass_style_storage.cpp +++ b/aegisub/src/ass_style_storage.cpp @@ -42,8 +42,10 @@ #include "text_file_writer.h" #include "utils.h" +#include + #include -#include +#include AssStyleStorage::~AssStyleStorage() { delete_clear(style); @@ -52,16 +54,14 @@ AssStyleStorage::~AssStyleStorage() { void AssStyleStorage::Save() const { if (storage_name.empty()) return; - wxString dirname = StandardPaths::DecodePath("?user/catalog/"); - if (!wxDirExists(dirname) && !wxMkdir(dirname)) - throw "Failed creating directory for style catalogs"; + agi::fs::CreateDirectory(StandardPaths::DecodePath("?user/catalog/")); TextFileWriter file(StandardPaths::DecodePath("?user/catalog/" + storage_name + ".sty"), "UTF-8"); for (const AssStyle *cur : style) file.WriteLineToFile(cur->GetEntryData()); } -void AssStyleStorage::Load(wxString const& name) { +void AssStyleStorage::Load(std::string const& name) { storage_name = name; Clear(); @@ -69,17 +69,14 @@ void AssStyleStorage::Load(wxString const& name) { TextFileReader file(StandardPaths::DecodePath("?user/catalog/" + name + ".sty"), "UTF-8"); while (file.HasMoreLines()) { - wxString data = file.ReadLineFromFile(); - if (data.StartsWith("Style:")) { - try { - style.push_back(new AssStyle(data)); - } catch(...) { - /* just ignore invalid lines for now */ - } + try { + style.push_back(new AssStyle(file.ReadLineFromFile())); + } catch(...) { + /* just ignore invalid lines for now */ } } } - catch (agi::FileNotAccessibleError const&) { + catch (agi::fs::FileNotAccessible const&) { // Just treat a missing file as an empty file } } diff --git a/aegisub/src/ass_style_storage.h b/aegisub/src/ass_style_storage.h index 1015e2665..d6dab605e 100644 --- a/aegisub/src/ass_style_storage.h +++ b/aegisub/src/ass_style_storage.h @@ -36,12 +36,10 @@ #include #include -#include - class AssStyle; class AssStyleStorage { - wxString storage_name; + std::string storage_name; std::deque style; public: @@ -77,5 +75,5 @@ public: /// Load stored styles from a file /// @param name Catalog name (note: not file name) - void Load(wxString const& name); + void Load(std::string const& name); }; diff --git a/aegisub/src/ass_time.cpp b/aegisub/src/ass_time.cpp index 7c38b6634..801df240b 100644 --- a/aegisub/src/ass_time.cpp +++ b/aegisub/src/ass_time.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2005, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2013, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. +// 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/ @@ -36,61 +23,64 @@ #include "ass_time.h" -#include -#include - -#include - #include "utils.h" +#include + +#include +#include +#include +#include +#include + AssTime::AssTime(int time) : time(mid(0, time, 10 * 60 * 60 * 1000 - 1)) { } -AssTime::AssTime(wxString const& text) +AssTime::AssTime(std::string const& text) : time(0) { - size_t pos = 0, end = 0; - - int colons = text.Freq(':'); - - // Set start so that there are only two colons at most - for (; colons > 2; --colons) pos = text.find(':', pos) + 1; - - // Hours - if (colons == 2) { - while (text[end++] != ':') { } - time += AegiStringToInt(text, pos, end) * 60 * 60 * 1000; - pos = end; + int after_decimal = -1; + int current = 0; + for (char c : text | boost::adaptors::filtered(boost::is_any_of(":0123456789."))) { + if (c == ':') { + time = time * 60 + current; + current = 0; + } + else if (c == '.') { + time = (time * 60 + current) * 1000; + current = 0; + after_decimal = 100; + } + else if (after_decimal < 0) { + current *= 10; + current += c - '0'; + } + else { + time += (c - '0') * after_decimal; + after_decimal /= 10; + } } - // Minutes - if (colons >= 1) { - while (text[end++] != ':') { } - time += AegiStringToInt(text, pos, end) * 60 * 1000; - } - - // Milliseconds (includes seconds) - time += AegiStringToFix(text, 3, end, text.size()); + // Never saw a decimal, so convert now to ms + if (after_decimal < 0) + time = (time * 60 + current) * 1000; // Limit to the valid range time = mid(0, time, 10 * 60 * 60 * 1000 - 1); } -wxString AssTime::GetAssFormated(bool msPrecision) const { - wxChar ret[] = { - '0' + GetTimeHours(), - ':', - '0' + (time % (60 * 60 * 1000)) / (60 * 1000 * 10), - '0' + (time % (10 * 60 * 1000)) / (60 * 1000), - ':', - '0' + (time % (60 * 1000)) / (1000 * 10), - '0' + (time % (10 * 1000)) / 1000, - '.', - '0' + (time % 1000) / 100, - '0' + (time % 100) / 10, - '0' + time % 10 - }; - - return wxString(ret, msPrecision ? 11 : 10); +std::string AssTime::GetAssFormated(bool msPrecision) const { + std::string ret(10 + msPrecision, ':'); + ret[0] = '0' + GetTimeHours(); + ret[2] = '0' + (time % (60 * 60 * 1000)) / (60 * 1000 * 10); + ret[3] = '0' + (time % (10 * 60 * 1000)) / (60 * 1000); + ret[5] = '0' + (time % (60 * 1000)) / (1000 * 10); + ret[6] = '0' + (time % (10 * 1000)) / 1000; + ret[7] = '.'; + ret[8] = '0' + (time % 1000) / 100; + ret[9] = '0' + (time % 100) / 10; + if (msPrecision) + ret[10] = '0' + time % 10; + return ret; } int AssTime::GetTimeHours() const { return time / 3600000; } @@ -99,25 +89,27 @@ int AssTime::GetTimeSeconds() const { return (time % 60000) / 1000; } int AssTime::GetTimeMiliseconds() const { return (time % 1000); } int AssTime::GetTimeCentiseconds() const { return (time % 1000) / 10; } -SmpteFormatter::SmpteFormatter(agi::vfr::Framerate fps, char sep) +SmpteFormatter::SmpteFormatter(agi::vfr::Framerate fps, std::string const& sep) : fps(fps) , sep(sep) { } -wxString SmpteFormatter::ToSMPTE(AssTime time) const { +std::string SmpteFormatter::ToSMPTE(AssTime time) const { int h=0, m=0, s=0, f=0; fps.SmpteAtTime(time, &h, &m, &s, &f); - return wxString::Format("%02d%c%02d%c%02d%c%02d", h, sep, m, sep, s, sep, f); + return str(boost::format("%02d%s%02d%s%02d%c%02d") % h % sep % m % sep % s % sep % f); } -AssTime SmpteFormatter::FromSMPTE(wxString const& str) const { - long h, m, s, f; - wxArrayString toks = wxStringTokenize(str, sep); +AssTime SmpteFormatter::FromSMPTE(std::string const& str) const { + std::vector toks; + boost::split(toks, str, boost::is_any_of(sep)); if (toks.size() != 4) return 0; - toks[0].ToLong(&h); - toks[1].ToLong(&m); - toks[2].ToLong(&s); - toks[3].ToLong(&f); + + int h, m, s, f; + agi::util::try_parse(toks[0], &h); + agi::util::try_parse(toks[1], &m); + agi::util::try_parse(toks[2], &s); + agi::util::try_parse(toks[3], &f); return fps.TimeAtSmpte(h, m, s, f); } diff --git a/aegisub/src/ass_time.h b/aegisub/src/ass_time.h index 44e0372c7..54d29ba76 100644 --- a/aegisub/src/ass_time.h +++ b/aegisub/src/ass_time.h @@ -1,29 +1,16 @@ -// Copyright (c) 2005, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2013, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. +// 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/ @@ -34,7 +21,7 @@ #pragma once -#include +#include #include @@ -44,7 +31,7 @@ class AssTime { public: AssTime(int ms = 0); - AssTime(wxString const& text); + AssTime(std::string const& text); /// Get millisecond, rounded to centisecond precision operator int() const { return time / 10 * 10; } @@ -57,7 +44,7 @@ public: /// Return the time as a string /// @param ms Use milliseconds precision, for non-ASS formats - wxString GetAssFormated(bool ms=false) const; + std::string GetAssFormated(bool ms=false) const; }; /// @class SmpteFormatter @@ -66,13 +53,13 @@ class SmpteFormatter { /// Frame rate to use agi::vfr::Framerate fps; /// Separator character - char sep; + std::string sep; public: - SmpteFormatter(agi::vfr::Framerate fps, char sep=':'); + SmpteFormatter(agi::vfr::Framerate fps, std::string const& sep=":"); /// Convert an AssTime to a SMPTE timecode - wxString ToSMPTE(AssTime time) const; + std::string ToSMPTE(AssTime time) const; /// Convert a SMPTE timecode to an AssTime - AssTime FromSMPTE(wxString const& str) const; + AssTime FromSMPTE(std::string const& str) const; }; diff --git a/aegisub/src/audio_controller.cpp b/aegisub/src/audio_controller.cpp index 446fc626b..dc3427362 100644 --- a/aegisub/src/audio_controller.cpp +++ b/aegisub/src/audio_controller.cpp @@ -34,14 +34,9 @@ #include "config.h" -#include - -#include - -#include +#include "audio_controller.h" #include "ass_file.h" -#include "audio_controller.h" #include "audio_provider_dummy.h" #include "audio_timing.h" #include "compat.h" @@ -51,10 +46,14 @@ #include "pen.h" #include "options.h" #include "selection_controller.h" -#include "standard_paths.h" #include "utils.h" #include "video_context.h" +#include +#include + +#include + AudioController::AudioController(agi::Context *context) : context(context) , subtitle_save_slot(context->ass->AddFileSaveListener(&AudioController::OnSubtitlesSave, this)) @@ -81,13 +80,11 @@ AudioController::AudioController(agi::Context *context) #endif } - AudioController::~AudioController() { CloseAudio(); } - void AudioController::OnPlaybackTimer(wxTimerEvent &) { int64_t pos = player->GetCurrentPosition(); @@ -105,7 +102,6 @@ void AudioController::OnPlaybackTimer(wxTimerEvent &) } } - #ifdef wxHAS_POWER_EVENTS void AudioController::OnComputerSuspending(wxPowerEvent &) { @@ -114,7 +110,6 @@ void AudioController::OnComputerSuspending(wxPowerEvent &) player = 0; } - void AudioController::OnComputerResuming(wxPowerEvent &) { if (provider) @@ -154,25 +149,25 @@ void AudioController::OnAudioProviderChanged() { if (IsAudioOpen()) // url is cloned because CloseAudio clears it and OpenAudio takes a const reference - OpenAudio(audio_url.Clone()); + OpenAudio(agi::fs::path(audio_url)); } -void AudioController::OpenAudio(const wxString &url) +void AudioController::OpenAudio(agi::fs::path const& url) { - if (!url) + if (url.empty()) throw agi::InternalError("AudioController::OpenAudio() was passed an empty string. This must not happen.", 0); AudioProvider *new_provider = 0; try { new_provider = AudioProviderFactory::GetProvider(url); - StandardPaths::SetPathValue("?audio", wxFileName(url).GetPath()); + config::path->SetToken("?audio", url); } catch (agi::UserCancelException const&) { throw; } catch (...) { - config::mru->Remove("Audio", from_wx(url)); + config::mru->Remove("Audio", url); throw; } @@ -192,11 +187,10 @@ void AudioController::OpenAudio(const wxString &url) audio_url = url; - config::mru->Add("Audio", from_wx(url)); + config::mru->Add("Audio", url); try { - // Tell listeners about this. AnnounceAudioOpen(provider); } catch (...) @@ -206,7 +200,6 @@ void AudioController::OpenAudio(const wxString &url) } } - void AudioController::CloseAudio() { Stop(); @@ -218,7 +211,7 @@ void AudioController::CloseAudio() audio_url.clear(); - StandardPaths::SetPathValue("?audio", ""); + config::path->SetToken("?audio", ""); AnnounceAudioClose(); } @@ -229,12 +222,6 @@ bool AudioController::IsAudioOpen() const return player && provider; } - -wxString AudioController::GetAudioURL() const -{ - return audio_url; -} - void AudioController::SetTimingController(AudioTimingController *new_controller) { if (timing_controller.get() != new_controller) { @@ -259,13 +246,9 @@ void AudioController::OnTimingControllerUpdatedPrimaryRange() void AudioController::OnSubtitlesSave() { if (IsAudioOpen()) - { - context->ass->SetScriptInfo("Audio URI", MakeRelativePath(audio_url, context->ass->filename)); - } + context->ass->SetScriptInfo("Audio URI", config::path->MakeRelative(audio_url, "?script").generic_string()); else - { context->ass->SetScriptInfo("Audio URI", ""); - } } void AudioController::PlayRange(const TimeRange &range) @@ -402,13 +385,13 @@ int64_t AudioController::MillisecondsFromSamples(int64_t samples) const return millisamples / sr; } -void AudioController::SaveClip(wxString const& filename, TimeRange const& range) const +void AudioController::SaveClip(agi::fs::path const& filename, TimeRange const& range) const { int64_t start_sample = SamplesFromMilliseconds(range.begin()); int64_t end_sample = SamplesFromMilliseconds(range.end()); if (filename.empty() || start_sample > provider->GetNumSamples() || range.length() == 0) return; - agi::io::Save outfile(from_wx(filename), true); + agi::io::Save outfile(filename, true); std::ofstream& out(outfile.Get()); size_t bytes_per_sample = provider->GetBytesPerSample() * provider->GetChannels(); diff --git a/aegisub/src/audio_controller.h b/aegisub/src/audio_controller.h index e63ed1814..b804e073c 100644 --- a/aegisub/src/audio_controller.h +++ b/aegisub/src/audio_controller.h @@ -33,20 +33,16 @@ #pragma once -#include +#include #include #include -#include -#include #include -#include #include -#include #include #include -#include +#include #include class AudioPlayer; @@ -96,10 +92,10 @@ class AudioController : public wxEvtHandler { AudioProvider *provider; /// The current timing mode, if any; owned by the audio controller - agi::scoped_ptr timing_controller; + std::unique_ptr timing_controller; /// The URL of the currently open audio, if any - wxString audio_url; + agi::fs::path audio_url; enum PlaybackMode { @@ -157,13 +153,9 @@ public: /// @brief Destructor ~AudioController(); - /// @brief Open an audio stream /// @param url URL of the stream to open - /// - /// The URL can either be a plain filename (with no qualifiers) or one - /// recognised by various providers. - void OpenAudio(const wxString &url); + void OpenAudio(agi::fs::path const& url); /// @brief Closes the current audio stream void CloseAudio(); @@ -177,7 +169,7 @@ public: /// /// The returned URL can be passed into OpenAudio() later to open the same /// stream again. - wxString GetAudioURL() const; + agi::fs::path GetAudioURL() const { return audio_url; } /// @brief Start or restart audio playback, playing a range @@ -263,7 +255,7 @@ public: /// @brief Save a portion of the decoded loaded audio to a wav file /// @param filename File to save to /// @param range Time range to save - void SaveClip(wxString const& filename, TimeRange const& range) const; + void SaveClip(agi::fs::path const& filename, TimeRange const& range) const; DEFINE_SIGNAL_ADDERS(AnnounceAudioOpen, AddAudioOpenListener) DEFINE_SIGNAL_ADDERS(AnnounceAudioClose, AddAudioCloseListener) diff --git a/aegisub/src/audio_display.cpp b/aegisub/src/audio_display.cpp index 5de97051b..fc512a1ef 100644 --- a/aegisub/src/audio_display.cpp +++ b/aegisub/src/audio_display.cpp @@ -963,7 +963,7 @@ void AudioDisplay::SetTrackCursor(int new_pos, bool show_time) if (show_time) { AssTime new_label_time = TimeFromAbsoluteX(track_cursor_pos); - track_cursor_label = new_label_time.GetAssFormated(); + track_cursor_label = to_wx(new_label_time.GetAssFormated()); track_cursor_label_rect.x += new_pos - old_pos; RefreshRect(track_cursor_label_rect, false); } diff --git a/aegisub/src/audio_player.cpp b/aegisub/src/audio_player.cpp index 3d27ff9c7..951c15c0c 100644 --- a/aegisub/src/audio_player.cpp +++ b/aegisub/src/audio_player.cpp @@ -44,7 +44,6 @@ #include "audio_player_pulse.h" #include "audio_controller.h" -#include "compat.h" #include "options.h" AudioPlayer::AudioPlayer(AudioProvider *provider) diff --git a/aegisub/src/audio_player_alsa.cpp b/aegisub/src/audio_player_alsa.cpp index c8f0ed5fd..cba6432c4 100644 --- a/aegisub/src/audio_player_alsa.cpp +++ b/aegisub/src/audio_player_alsa.cpp @@ -369,8 +369,7 @@ AlsaPlayer::AlsaPlayer(AudioProvider *provider) { ps->provider = provider; - wxString device_name = to_wx(OPT_GET("Player/Audio/ALSA/Device")->GetString()); - ps->device_name = std::string(device_name.utf8_str()); + ps->device_name = OPT_GET("Player/Audio/ALSA/Device")->GetString(); if (pthread_create(&thread, 0, &playback_thread, ps.get()) != 0) throw agi::AudioPlayerOpenError("AlsaPlayer: Creating the playback thread failed", 0); diff --git a/aegisub/src/audio_player_oss.cpp b/aegisub/src/audio_player_oss.cpp index 1c2d89ca4..92df02acf 100644 --- a/aegisub/src/audio_player_oss.cpp +++ b/aegisub/src/audio_player_oss.cpp @@ -75,7 +75,7 @@ void OSSPlayer::OpenStream() // Open device wxString device = to_wx(OPT_GET("Player/Audio/OSS/Device")->GetString()); - dspdev = ::open(device.mb_str(wxConvUTF8), O_WRONLY, 0); + dspdev = ::open(device.utf8_str(), O_WRONLY, 0); if (dspdev < 0) { throw OSSError("OSS player: opening device failed", 0); } diff --git a/aegisub/src/audio_provider.cpp b/aegisub/src/audio_provider.cpp index d9e7d2b5e..f6a2b5317 100644 --- a/aegisub/src/audio_provider.cpp +++ b/aegisub/src/audio_provider.cpp @@ -32,11 +32,8 @@ /// @ingroup audio_input /// - #include "config.h" -#include - #include "audio_provider_avs.h" #include "audio_provider_convert.h" #include "audio_provider_dummy.h" @@ -47,13 +44,13 @@ #include "audio_provider_ram.h" #include "audio_controller.h" -#include "compat.h" #include "dialog_progress.h" #include "frame_main.h" #include "main.h" #include "options.h" #include "utils.h" +#include #include void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const { @@ -126,7 +123,7 @@ struct provider_creator { LOG_I("audio_provider") << "Using audio provider: " << name; return provider; } - catch (agi::FileNotFoundError const& err) { + catch (agi::fs::FileNotFound const& err) { LOG_D("audio_provider") << err.GetChainedMessage(); msg += name + ": " + err.GetMessage() + " not found.\n"; } @@ -147,7 +144,7 @@ struct provider_creator { }; } -AudioProvider *AudioProviderFactory::GetProvider(wxString const& filename) { +AudioProvider *AudioProviderFactory::GetProvider(agi::fs::path const& filename) { provider_creator creator; AudioProvider *provider = nullptr; @@ -172,7 +169,7 @@ AudioProvider *AudioProviderFactory::GetProvider(wxString const& filename) { throw agi::AudioProviderOpenError(creator.msg, 0); if (creator.found_file) throw agi::AudioDataNotFoundError(creator.msg, 0); - throw agi::FileNotFoundError(from_wx(filename)); + throw agi::fs::FileNotFound(filename); } bool needsCache = provider->NeedsCache(); @@ -206,4 +203,4 @@ void AudioProviderFactory::RegisterProviders() { #endif } -template<> AudioProviderFactory::map *FactoryBase::classes = nullptr; +template<> AudioProviderFactory::map *FactoryBase::classes = nullptr; diff --git a/aegisub/src/audio_provider_avs.cpp b/aegisub/src/audio_provider_avs.cpp index dd3178f65..07ee347f2 100644 --- a/aegisub/src/audio_provider_avs.cpp +++ b/aegisub/src/audio_provider_avs.cpp @@ -38,38 +38,38 @@ #include "audio_provider_avs.h" #include "audio_controller.h" -#include "charset_conv.h" -#include "compat.h" #include "options.h" #include "standard_paths.h" #include "utils.h" -#include +#include +#include +#include -AvisynthAudioProvider::AvisynthAudioProvider(wxString filename) { +#include + +AvisynthAudioProvider::AvisynthAudioProvider(agi::fs::path const& filename) { this->filename = filename; - wxMutexLocker lock(avs_wrapper.GetMutex()); + agi::acs::CheckFileRead(filename); - wxFileName fn(filename); - if (!fn.FileExists()) - throw agi::FileNotFoundError(from_wx(filename)); + std::lock_guard lock(avs_wrapper.GetMutex()); try { IScriptEnvironment *env = avs_wrapper.GetEnv(); // Include - if (filename.EndsWith(".avs")) - LoadFromClip(env->Invoke("Import", env->SaveString(fn.GetShortPath().mb_str(csConvLocal)))); + if (agi::fs::HasExtension(filename, "avs")) + LoadFromClip(env->Invoke("Import", env->SaveString(agi::fs::ShortName(filename).c_str()))); // Use DirectShowSource else { const char * argnames[3] = { 0, "video", "audio" }; - AVSValue args[3] = { env->SaveString(fn.GetShortPath().mb_str(csConvLocal)), false, true }; + AVSValue args[3] = { env->SaveString(agi::fs::ShortName(filename).c_str()), false, true }; // Load DirectShowSource.dll from app dir if it exists - wxFileName dsspath(StandardPaths::DecodePath("?data/DirectShowSource.dll")); - if (dsspath.FileExists()) - env->Invoke("LoadPlugin", env->SaveString(dsspath.GetShortPath().mb_str(csConvLocal))); + agi::fs::path dsspath(StandardPaths::DecodePath("?data/DirectShowSource.dll")); + if (agi::fs::FileExists(dsspath)) + env->Invoke("LoadPlugin", env->SaveString(agi::fs::ShortName(dsspath).c_str())); // Load audio with DSS if it exists if (env->FunctionExists("DirectShowSource")) diff --git a/aegisub/src/audio_provider_avs.h b/aegisub/src/audio_provider_avs.h index 8784aa6d4..3e0cb58b0 100644 --- a/aegisub/src/audio_provider_avs.h +++ b/aegisub/src/audio_provider_avs.h @@ -46,7 +46,7 @@ class AvisynthAudioProvider : public AudioProvider { void FillBuffer(void *buf, int64_t start, int64_t count) const; public: - AvisynthAudioProvider(wxString filename); + AvisynthAudioProvider(agi::fs::path const& filename); bool AreSamplesNativeEndian() const { return true; } bool NeedsCache() const { return true; } diff --git a/aegisub/src/audio_provider_convert.cpp b/aegisub/src/audio_provider_convert.cpp index 004972148..fbab34c2d 100644 --- a/aegisub/src/audio_provider_convert.cpp +++ b/aegisub/src/audio_provider_convert.cpp @@ -46,7 +46,7 @@ public: } bool AreSamplesNativeEndian() const { return true; } - wxString GetFilename() const { return source->GetFilename(); } + agi::fs::path GetFilename() const { return source->GetFilename(); } }; /// Anything integral -> 16 bit signed machine-endian audio converter diff --git a/aegisub/src/audio_provider_dummy.cpp b/aegisub/src/audio_provider_dummy.cpp index 28a672e91..d9f60d6bb 100644 --- a/aegisub/src/audio_provider_dummy.cpp +++ b/aegisub/src/audio_provider_dummy.cpp @@ -37,7 +37,9 @@ #include "audio_provider_dummy.h" #include "utils.h" -#include +#include + +#include /* * scheme ::= "dummy-audio" ":" signal-specifier "?" signal-parameters @@ -59,13 +61,11 @@ * in every channel even if one would be LFE. * "ln", length of signal in samples. ln/sr gives signal length in seconds. */ -DummyAudioProvider::DummyAudioProvider(wxString uri) -{ - wxURI parsed(uri); - if (parsed.GetScheme() != "dummy-audio") - throw agi::FileNotFoundError("Not a dummy audio URI"); +DummyAudioProvider::DummyAudioProvider(agi::fs::path const& uri) { + if (!boost::starts_with(uri.string(), "dummy-audio:")) + throw agi::fs::FileNotFound(std::string("Not a dummy audio URI")); - noise = parsed.GetPath() == "noise"; + noise = boost::contains(uri.string(), ":noise?"); channels = 1; sample_rate = 44100; bytes_per_sample = 2; diff --git a/aegisub/src/audio_provider_dummy.h b/aegisub/src/audio_provider_dummy.h index 1dbcaf975..05d5f02f8 100644 --- a/aegisub/src/audio_provider_dummy.h +++ b/aegisub/src/audio_provider_dummy.h @@ -39,7 +39,7 @@ class DummyAudioProvider : public AudioProvider { void FillBuffer(void *buf, int64_t start, int64_t count) const; public: - DummyAudioProvider(wxString uri); + DummyAudioProvider(agi::fs::path const& uri); bool AreSamplesNativeEndian() const { return true; } }; diff --git a/aegisub/src/audio_provider_ffmpegsource.cpp b/aegisub/src/audio_provider_ffmpegsource.cpp index 79e07c072..203110a03 100644 --- a/aegisub/src/audio_provider_ffmpegsource.cpp +++ b/aegisub/src/audio_provider_ffmpegsource.cpp @@ -35,22 +35,18 @@ #include "config.h" #ifdef WITH_FFMS2 - -#ifdef WIN32 -#include -#endif - -#include - #include "audio_provider_ffmpegsource.h" #include "audio_controller.h" -#include "compat.h" #include "options.h" +#include + +#include + /// @brief Constructor /// @param filename The filename to open -FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(wxString filename) try +FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(agi::fs::path const& filename) try : AudioSource(nullptr, FFMS_DestroyAudioSource) { ErrInfo.Buffer = FFMSErrMsg; @@ -61,26 +57,24 @@ FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(wxString filename) try LoadAudio(filename); } -catch (wxString const& err) { - throw agi::AudioProviderOpenError(from_wx(err), 0); +catch (std::string const& err) { + throw agi::AudioProviderOpenError(err, 0); } catch (const char *err) { throw agi::AudioProviderOpenError(err, 0); } -void FFmpegSourceAudioProvider::LoadAudio(wxString filename) { - wxString FileNameShort = wxFileName(filename).GetShortPath(); - - FFMS_Indexer *Indexer = FFMS_CreateIndexer(FileNameShort.utf8_str(), &ErrInfo); +void FFmpegSourceAudioProvider::LoadAudio(agi::fs::path const& filename) { + FFMS_Indexer *Indexer = FFMS_CreateIndexer(filename.string().c_str(), &ErrInfo); if (!Indexer) { if (ErrInfo.SubType == FFMS_ERROR_FILE_READ) - throw agi::FileNotFoundError(ErrInfo.Buffer); + throw agi::fs::FileNotFound(std::string(ErrInfo.Buffer)); else throw agi::AudioDataNotFoundError(ErrInfo.Buffer, 0); } - std::map TrackList = GetTracksOfType(Indexer, FFMS_TYPE_AUDIO); - if (TrackList.size() <= 0) + std::map TrackList = GetTracksOfType(Indexer, FFMS_TYPE_AUDIO); + if (TrackList.empty()) throw agi::AudioDataNotFoundError("no audio tracks found", 0); // initialize the track number to an invalid value so we can detect later on @@ -94,13 +88,13 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) { } // generate a name for the cache file - wxString CacheName = GetCacheFilename(filename); + agi::fs::path CacheName = GetCacheFilename(filename); // try to read index agi::scoped_holder - Index(FFMS_ReadIndex(CacheName.utf8_str(), &ErrInfo), FFMS_DestroyIndex); + Index(FFMS_ReadIndex(CacheName.string().c_str(), &ErrInfo), FFMS_DestroyIndex); - if (Index && FFMS_IndexBelongsToFile(Index, FileNameShort.utf8_str(), &ErrInfo)) + if (Index && FFMS_IndexBelongsToFile(Index, filename.string().c_str(), &ErrInfo)) Index = nullptr; // index valid but track number still not set? @@ -143,16 +137,13 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) { if (TrackNumber == FFMS_TRACKMASK_ALL) TrackNumber = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_AUDIO, &ErrInfo); } - else { + else FFMS_CancelIndexing(Indexer); - } // update access time of index file so it won't get cleaned away - if (!wxFileName(CacheName).Touch()) { - // warn user? - } + agi::fs::Touch(CacheName); - AudioSource = FFMS_CreateAudioSource(FileNameShort.utf8_str(), TrackNumber, Index, -1, &ErrInfo); + AudioSource = FFMS_CreateAudioSource(filename.string().c_str(), TrackNumber, Index, -1, &ErrInfo); if (!AudioSource) throw agi::AudioProviderOpenError(std::string("Failed to open audio track: ") + ErrInfo.Buffer, 0); @@ -192,8 +183,7 @@ void FFmpegSourceAudioProvider::LoadAudio(wxString filename) { } void FFmpegSourceAudioProvider::FillBuffer(void *Buf, int64_t Start, int64_t Count) const { - if (FFMS_GetAudio(AudioSource, Buf, Start, Count, &ErrInfo)) { + if (FFMS_GetAudio(AudioSource, Buf, Start, Count, &ErrInfo)) throw AudioDecodeError(std::string("Failed to get audio samples: ") + ErrInfo.Buffer); - } } #endif /* WITH_FFMS2 */ diff --git a/aegisub/src/audio_provider_ffmpegsource.h b/aegisub/src/audio_provider_ffmpegsource.h index 73fef6b34..95fa8da18 100644 --- a/aegisub/src/audio_provider_ffmpegsource.h +++ b/aegisub/src/audio_provider_ffmpegsource.h @@ -34,8 +34,8 @@ #ifdef WITH_FFMS2 #include "include/aegisub/audio_provider.h" -#include "ffmpegsource_common.h" +#include "ffmpegsource_common.h" /// @class FFmpegSourceAudioProvider /// @brief Implements audio loading with the FFMS library. @@ -46,11 +46,11 @@ class FFmpegSourceAudioProvider : public AudioProvider, FFmpegSourceProvider { mutable char FFMSErrMsg[1024]; ///< FFMS error message mutable FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages - void LoadAudio(wxString filename); + void LoadAudio(agi::fs::path const& filename); void FillBuffer(void *buf, int64_t start, int64_t count) const; public: - FFmpegSourceAudioProvider(wxString filename); + FFmpegSourceAudioProvider(agi::fs::path const& filename); /// @brief Checks sample endianness /// @return Returns true. diff --git a/aegisub/src/audio_provider_hd.cpp b/aegisub/src/audio_provider_hd.cpp index 82553d6b9..54989727c 100644 --- a/aegisub/src/audio_provider_hd.cpp +++ b/aegisub/src/audio_provider_hd.cpp @@ -36,50 +36,43 @@ #include "audio_provider_hd.h" -#include -#include - -#include -#include - #include "audio_controller.h" #include "audio_provider_pcm.h" #include "compat.h" #include "options.h" -#include "standard_paths.h" #include "utils.h" -namespace { -wxString cache_dir() { - wxString path = to_wx(OPT_GET("Audio/Cache/HD/Location")->GetString()); - if (path == "default") - path = "?temp/"; +#include +#include +#include +#include +#include +#include - return DecodeRelativePath(StandardPaths::DecodePath(path), StandardPaths::DecodePath("?user/")); +#include +#include +#include + +namespace { +agi::fs::path cache_dir() { + std::string path = OPT_GET("Audio/Cache/HD/Location")->GetString(); + if (path == "default") + path = "?temp"; + + return config::path->MakeAbsolute(config::path->Decode(path), "?temp"); } -wxString cache_path() { - wxString pattern = to_wx(OPT_GET("Audio/Cache/HD/Name")->GetString()); - if (pattern.Find("%02i") == wxNOT_FOUND) pattern = "audio%02i.tmp"; - - // Try from 00 to 99 - for (int i=0;i<100;i++) { - // File exists? - wxFileName curNameTry(cache_dir(), wxString::Format(pattern, i)); -#if wxCHECK_VERSION(2, 9, 4) - if (!curNameTry.Exists()) -#else - if (!curNameTry.FileExists() && !curNameTry.DirExists()) -#endif - return curNameTry.GetFullPath(); - } - return ""; +agi::fs::path cache_path() { + std::string pattern = OPT_GET("Audio/Cache/HD/Name")->GetString(); + if (!boost::contains(pattern, "%02i")) pattern = "audio%02i.tmp"; + boost::replace_all(pattern, "%02i", "%%%%-%%%%-%%%%-%%%%"); + return unique_path(cache_dir()/pattern); } /// A PCM audio provider for raw dumps with no header class RawAudioProvider : public PCMAudioProvider { public: - RawAudioProvider(wxString const& cache_filename, AudioProvider *src) + RawAudioProvider(agi::fs::path const& cache_filename, AudioProvider *src) : PCMAudioProvider(cache_filename) { bytes_per_sample = src->GetBytesPerSample(); @@ -110,30 +103,27 @@ HDAudioProvider::HDAudioProvider(AudioProvider *src, agi::BackgroundRunner *br) float_samples = source->AreSamplesFloat(); // Check free space - wxDiskspaceSize_t freespace; - if (wxGetDiskSpace(cache_dir(), 0, &freespace)) { - if (num_samples * channels * bytes_per_sample > freespace) - throw agi::AudioCacheOpenError("Not enough free disk space in " + from_wx(cache_dir()) + " to cache the audio", 0); - } + if ((uint64_t)num_samples * channels * bytes_per_sample > agi::fs::FreeSpace(cache_dir())) + throw agi::AudioCacheOpenError("Not enough free disk space in " + cache_dir().string() + " to cache the audio", 0); diskCacheFilename = cache_path(); try { { - agi::io::Save out(from_wx(diskCacheFilename), true); + agi::io::Save out(diskCacheFilename, true); br->Run(bind(&HDAudioProvider::FillCache, this, src, &out.Get(), std::placeholders::_1)); } cache_provider.reset(new RawAudioProvider(diskCacheFilename, src)); } catch (...) { - wxRemoveFile(diskCacheFilename); + agi::fs::Remove(diskCacheFilename); throw; } } HDAudioProvider::~HDAudioProvider() { cache_provider.reset(); // explicitly close the file so we can delete it - wxRemoveFile(diskCacheFilename); + agi::fs::Remove(diskCacheFilename); } void HDAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const { diff --git a/aegisub/src/audio_provider_hd.h b/aegisub/src/audio_provider_hd.h index 1f878702a..8162ff1eb 100644 --- a/aegisub/src/audio_provider_hd.h +++ b/aegisub/src/audio_provider_hd.h @@ -32,8 +32,6 @@ /// @ingroup audio_input /// -#include - #include "include/aegisub/audio_provider.h" #include @@ -45,7 +43,7 @@ namespace agi { class HDAudioProvider : public AudioProvider { /// Name of the file which the decoded audio is written to - wxString diskCacheFilename; + agi::fs::path diskCacheFilename; /// Audio provider which reads from the decoded cache agi::scoped_ptr cache_provider; diff --git a/aegisub/src/audio_provider_lock.cpp b/aegisub/src/audio_provider_lock.cpp index 12e948d13..44100c67d 100644 --- a/aegisub/src/audio_provider_lock.cpp +++ b/aegisub/src/audio_provider_lock.cpp @@ -29,6 +29,6 @@ LockAudioProvider::LockAudioProvider(AudioProvider *source) : source(source) { } void LockAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const { - wxMutexLocker lock(mutex); + std::unique_lock lock(mutex); source->GetAudio(buf, start, count); } diff --git a/aegisub/src/audio_provider_lock.h b/aegisub/src/audio_provider_lock.h index 3315d0d64..053ef51c1 100644 --- a/aegisub/src/audio_provider_lock.h +++ b/aegisub/src/audio_provider_lock.h @@ -18,13 +18,12 @@ #include "include/aegisub/audio_provider.h" -#include - -#include +#include +#include class LockAudioProvider : public AudioProvider { - agi::scoped_ptr source; - mutable wxMutex mutex; + std::unique_ptr source; + mutable std::mutex mutex; void FillBuffer(void *buf, int64_t start, int64_t count) const; public: diff --git a/aegisub/src/audio_provider_pcm.cpp b/aegisub/src/audio_provider_pcm.cpp index 59a35c81b..13da1844f 100644 --- a/aegisub/src/audio_provider_pcm.cpp +++ b/aegisub/src/audio_provider_pcm.cpp @@ -32,30 +32,26 @@ /// @ingroup audio_input /// - #include "config.h" +#include "audio_provider_pcm.h" + +#include "aegisub_endian.h" +#include "audio_controller.h" +#include "utils.h" + +#include +#include + #include #include -#ifndef __WINDOWS__ +#ifndef _WIN32 #include #include #include #endif -#include -#include -#include - -#include - -#include "aegisub_endian.h" -#include "audio_controller.h" -#include "audio_provider_pcm.h" -#include "compat.h" -#include "utils.h" - -PCMAudioProvider::PCMAudioProvider(const wxString &filename) +PCMAudioProvider::PCMAudioProvider(agi::fs::path const& filename) : current_mapping(0) , mapping_start(0) , mapping_length(0) @@ -73,13 +69,7 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename) 0); if (file_handle == INVALID_HANDLE_VALUE) - throw agi::FileNotFoundError(from_wx(filename)); - - LARGE_INTEGER li_file_size = {0}; - if (!GetFileSizeEx(file_handle, &li_file_size)) - throw agi::AudioProviderOpenError("Failed getting file size", 0); - - file_size = li_file_size.QuadPart; + throw agi::fs::FileNotFound(filename); file_mapping = CreateFileMapping( file_handle, @@ -91,24 +81,22 @@ PCMAudioProvider::PCMAudioProvider(const wxString &filename) if (file_mapping == 0) throw agi::AudioProviderOpenError("Failed creating file mapping", 0); #else -, file_handle(open(filename.mb_str(*wxConvFileName), O_RDONLY), close) +, file_handle(open(filename.c_str(), O_RDONLY), close) { if (file_handle == -1) - throw agi::FileNotFoundError(from_wx(filename)); - - struct stat filestats; - memset(&filestats, 0, sizeof(filestats)); - if (fstat(file_handle, &filestats)) { - close(file_handle); - throw agi::AudioProviderOpenError("Could not stat file to get size", 0); - } - file_size = filestats.st_size; + throw agi::fs::FileNotFound(filename.string()); #endif float_samples = false; + + try { + file_size = agi::fs::Size(filename); + } + catch (agi::Exception const& e) { + throw agi::AudioPlayerOpenError("Could not get file size", e.Copy()); + } } -PCMAudioProvider::~PCMAudioProvider() -{ +PCMAudioProvider::~PCMAudioProvider() { #ifdef _WIN32 if (current_mapping) UnmapViewOfFile(current_mapping); @@ -118,11 +106,9 @@ PCMAudioProvider::~PCMAudioProvider() #endif } -char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t range_length) const -{ - if (range_start + range_length > file_size) { +char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t range_length) const { + if (range_start + range_length > file_size) throw AudioDecodeError("Attempted to map beyond end of file"); - } // Check whether the requested range is already visible if (!current_mapping || range_start < mapping_start || range_start+range_length > mapping_start+(int64_t)mapping_length) { @@ -171,9 +157,8 @@ char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t rang current_mapping = mmap(0, mapping_length, PROT_READ, MAP_PRIVATE, file_handle, mapping_start); #endif - if (!current_mapping) { + if (!current_mapping) throw AudioDecodeError("Failed mapping a view of the file"); - } } assert(current_mapping); @@ -186,8 +171,7 @@ char * PCMAudioProvider::EnsureRangeAccessible(int64_t range_start, int64_t rang return ((char*)current_mapping) + rel_ofs; } -void PCMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const -{ +void PCMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const { // Read blocks from the file size_t index = 0; while (count > 0 && index < index_points.size()) { @@ -268,10 +252,10 @@ class RiffWavPCMAudioProvider : public PCMAudioProvider { public: - RiffWavPCMAudioProvider(const wxString &_filename) - : PCMAudioProvider(_filename) + RiffWavPCMAudioProvider(agi::fs::path const& filename) + : PCMAudioProvider(filename) { - filename = _filename; + this->filename = filename; // Read header void *filestart = EnsureRangeAccessible(0, sizeof(RIFFChunk)); @@ -345,8 +329,7 @@ public: } } - bool AreSamplesNativeEndian() const - { + bool AreSamplesNativeEndian() const { // 8 bit samples don't consider endianness if (bytes_per_sample < 2) return true; // Otherwise test whether we're little endian @@ -411,17 +394,16 @@ class Wave64AudioProvider : public PCMAudioProvider { uint64_t chunk_size; }; - inline bool CheckGuid(const uint8_t *guid1, const uint8_t *guid2) - { + bool CheckGuid(const uint8_t *guid1, const uint8_t *guid2) { return memcmp(guid1, guid2, 16) == 0; } public: - Wave64AudioProvider(const wxString &_filename) - : PCMAudioProvider(_filename) + Wave64AudioProvider(agi::fs::path const& filename) + : PCMAudioProvider(filename) { - filename = _filename; + this->filename = filename; int64_t smallest_possible_file = sizeof(RiffChunk) + sizeof(FormatChunk) + sizeof(DataChunk); @@ -496,8 +478,7 @@ public: } } - bool AreSamplesNativeEndian() const - { + bool AreSamplesNativeEndian() const { // 8 bit samples don't consider endianness if (bytes_per_sample < 2) return true; // Otherwise test whether we're little endian @@ -506,8 +487,7 @@ public: } }; -AudioProvider *CreatePCMAudioProvider(const wxString &filename) -{ +AudioProvider *CreatePCMAudioProvider(agi::fs::path const& filename) { bool wrong_file_type = true; std::string msg; diff --git a/aegisub/src/audio_provider_pcm.h b/aegisub/src/audio_provider_pcm.h index 758129cff..ab61363f8 100644 --- a/aegisub/src/audio_provider_pcm.h +++ b/aegisub/src/audio_provider_pcm.h @@ -34,9 +34,6 @@ #include -#include -#include - #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include @@ -63,7 +60,7 @@ class PCMAudioProvider : public AudioProvider { #endif protected: - PCMAudioProvider(const wxString &filename); // Create base object and open the file mapping + PCMAudioProvider(agi::fs::path const& filename); // Create base object and open the file mapping virtual ~PCMAudioProvider(); // Closes the file mapping char * EnsureRangeAccessible(int64_t range_start, int64_t range_length) const; // Ensure that the given range of bytes are accessible in the file mapping and return a pointer to the first byte of the requested range @@ -84,4 +81,4 @@ protected: }; // Construct the right PCM audio provider (if any) for the file -AudioProvider *CreatePCMAudioProvider(const wxString &filename); +AudioProvider *CreatePCMAudioProvider(agi::fs::path const& filename); diff --git a/aegisub/src/auto4_base.cpp b/aegisub/src/auto4_base.cpp index 9345fb1e4..455f2576a 100644 --- a/aegisub/src/auto4_base.cpp +++ b/aegisub/src/auto4_base.cpp @@ -36,35 +36,9 @@ #include "auto4_base.h" -#ifdef __WINDOWS__ -#include -#define WIN32_LEAN_AND_MEAN -#include - -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifndef __WINDOWS__ -#include -#include FT_FREETYPE_H -#endif - #include "ass_file.h" #include "ass_style.h" #include "command/command.h" -#include "compat.h" #include "dialog_progress.h" #include "include/aegisub/context.h" #include "options.h" @@ -73,8 +47,28 @@ #include "subtitle_format.h" #include "utils.h" +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#ifdef __WINDOWS__ +#define WIN32_LEAN_AND_MEAN +#include + +#include +#endif + namespace Automation4 { - bool CalculateTextExtents(AssStyle *style, wxString const& text, double &width, double &height, double &descent, double &extlead) + bool CalculateTextExtents(AssStyle *style, std::string const& text, double &width, double &height, double &descent, double &extlead) { width = height = descent = extlead = 0; @@ -105,27 +99,22 @@ namespace Automation4 { if (!thefont) return false; SelectObject(thedc, thefont); + std::wstring wtext(agi::charset::ConvertW(text)); SIZE sz; - size_t thetextlen = text.length(); - const TCHAR *thetext = text.wc_str(); if (spacing != 0 ) { width = 0; - for (unsigned int i = 0; i < thetextlen; i++) { - GetTextExtentPoint32(thedc, &thetext[i], 1, &sz); + for (auto c : wtext) { + GetTextExtentPoint32(thedc, &c, 1, &sz); width += sz.cx + spacing; height = sz.cy; } - } else { - GetTextExtentPoint32(thedc, thetext, (int)thetextlen, &sz); + } + else { + GetTextExtentPoint32(thedc, &wtext[0], (int)wtext.size(), &sz); width = sz.cx; height = sz.cy; } - // HACKISH FIX! This seems to work, but why? It shouldn't be needed?!? - //fontsize = style->fontsize; - //width = (int)(width * fontsize/height + 0.5); - //height = (int)(fontsize + 0.5); - TEXTMETRIC tm; GetTextMetrics(thedc, &tm); descent = tm.tmDescent; @@ -150,17 +139,18 @@ namespace Automation4 { style->italic ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL, style->bold ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL, style->underline, - style->font, + to_wx(style->font), wxFONTENCODING_SYSTEM); // FIXME! make sure to get the right encoding here, make some translation table between windows and wx encodings thedc.SetFont(thefont); + wxString wtext(to_wx(text)); if (spacing) { // If there's inter-character spacing, kerning info must not be used, so calculate width per character // NOTE: Is kerning actually done either way?! - for (unsigned int i = 0; i < text.length(); i++) { + for (auto const& wc : wtext) { int a, b, c, d; - thedc.GetTextExtent(text[i], &a, &b, &c, &d); - double scaling = fontsize / (double)(b>0?b:1); // semi-workaround for missing OS/2 table data for scaling + thedc.GetTextExtent(wc, &a, &b, &c, &d); + double scaling = fontsize / (double)(b > 0 ? b : 1); // semi-workaround for missing OS/2 table data for scaling width += (a + spacing)*scaling; height = b > height ? b*scaling : height; descent = c > descent ? c*scaling : descent; @@ -169,8 +159,8 @@ namespace Automation4 { } else { // If the inter-character spacing should be zero, kerning info can (and must) be used, so calculate everything in one go wxCoord lwidth, lheight, ldescent, lextlead; - thedc.GetTextExtent(text, &lwidth, &lheight, &ldescent, &lextlead); - double scaling = fontsize / (double)(lheight>0?lheight:1); // semi-workaround for missing OS/2 table data for scaling + thedc.GetTextExtent(wtext, &lwidth, &lheight, &ldescent, &lextlead); + double scaling = fontsize / (double)(lheight > 0 ? lheight : 1); // semi-workaround for missing OS/2 table data for scaling width = lwidth*scaling; height = lheight*scaling; descent = ldescent*scaling; extlead = lextlead*scaling; } #endif @@ -184,7 +174,7 @@ namespace Automation4 { return true; } - ExportFilter::ExportFilter(wxString const& name, wxString const& description, int priority) + ExportFilter::ExportFilter(std::string const& name, std::string const& description, int priority) : AssExportFilter(name, description, priority) { AssExportFilterChain::Register(this); @@ -195,16 +185,16 @@ namespace Automation4 { AssExportFilterChain::Unregister(this); } - wxString ExportFilter::GetScriptSettingsIdentifier() + std::string ExportFilter::GetScriptSettingsIdentifier() { - return inline_string_encode(wxString::Format("Automation Settings %s", GetName())); + return inline_string_encode("Automation Settings " + GetName()); } wxWindow* ExportFilter::GetConfigDialogWindow(wxWindow *parent, agi::Context *c) { config_dialog.reset(GenerateConfigDialog(parent, c)); if (config_dialog) { - wxString val = c->ass->GetScriptInfo(GetScriptSettingsIdentifier()); + std::string val = c->ass->GetScriptInfo(GetScriptSettingsIdentifier()); if (!val.empty()) config_dialog->Unserialise(val); return config_dialog->CreateWindow(parent); @@ -215,7 +205,7 @@ namespace Automation4 { void ExportFilter::LoadSettings(bool is_default, agi::Context *c) { if (config_dialog) { - wxString val = config_dialog->Serialise(); + std::string val = config_dialog->Serialise(); if (!val.empty()) c->ass->SetScriptInfo(GetScriptSettingsIdentifier(), val); } @@ -231,10 +221,10 @@ namespace Automation4 { void ProgressSink::ShowDialog(ScriptDialog *config_dialog) { - InvokeOnMainThread([=] { + agi::dispatch::Main().Sync([=] { wxDialog w; // container dialog box w.SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY); - w.Create(bsr->GetParentWindow(), -1, bsr->GetTitle()); + w.Create(bsr->GetParentWindow(), -1, to_wx(bsr->GetTitle())); wxBoxSizer *s = new wxBoxSizer(wxHORIZONTAL); // sizer for putting contents in wxWindow *ww = config_dialog->CreateWindow(&w); // generate actual dialog contents s->Add(ww, 0, wxALL, 5); // add contents to dialog @@ -247,12 +237,12 @@ namespace Automation4 { int ProgressSink::ShowDialog(wxDialog *dialog) { int ret = 0; - InvokeOnMainThread([&] { ret = dialog->ShowModal(); }); + agi::dispatch::Main().Sync([&] { ret = dialog->ShowModal(); }); return ret; } - BackgroundScriptRunner::BackgroundScriptRunner(wxWindow *parent, wxString const& title) - : impl(new DialogProgress(parent, title)) + BackgroundScriptRunner::BackgroundScriptRunner(wxWindow *parent, std::string const& title) + : impl(new DialogProgress(parent, to_wx(title))) { } @@ -279,26 +269,23 @@ namespace Automation4 { return impl.get(); } - wxString BackgroundScriptRunner::GetTitle() const + std::string BackgroundScriptRunner::GetTitle() const { - return impl->GetTitle(); + return from_wx(impl->GetTitle()); } // Script - Script::Script(wxString const& filename) + Script::Script(agi::fs::path const& filename) : filename(filename) { - // copied from auto3 - include_path.clear(); - include_path.EnsureFileAccessible(filename); - wxStringTokenizer toker(to_wx(OPT_GET("Path/Automation/Include")->GetString()), "|", wxTOKEN_STRTOK); - while (toker.HasMoreTokens()) { - // todo? make some error reporting here - wxFileName path(StandardPaths::DecodePath(toker.GetNextToken())); - if (!path.IsOk()) continue; - if (path.IsRelative()) continue; - if (!path.DirExists()) continue; - include_path.Add(path.GetLongPath()); + include_path.emplace_back(filename.parent_path()); + + std::string include_paths = OPT_GET("Path/Automation/Include")->GetString(); + boost::char_separator sep("|"); + for (auto const& tok : boost::tokenizer>(include_paths, sep)) { + auto path = StandardPaths::DecodePath(tok); + if (path.is_absolute() && agi::fs::DirectoryExists(path)) + include_path.emplace_back(std::move(path)); } } @@ -352,9 +339,8 @@ namespace Automation4 { return macros; } - // AutoloadScriptManager - AutoloadScriptManager::AutoloadScriptManager(wxString const& path) + AutoloadScriptManager::AutoloadScriptManager(std::string const& path) : path(path) { Reload(); @@ -366,31 +352,17 @@ namespace Automation4 { int error_count = 0; - wxStringTokenizer tok(path, "|", wxTOKEN_STRTOK); - while (tok.HasMoreTokens()) { - wxDir dir; - wxString dirname = StandardPaths::DecodePath(tok.GetNextToken()); - if (!dir.Exists(dirname)) { - //wxLogWarning("A directory was specified in the Automation autoload path, but it doesn't exist: %s", dirname.c_str()); - continue; - } - if (!dir.Open(dirname)) { - //wxLogWarning("Failed to open a directory in the Automation autoload path: %s", dirname.c_str()); - continue; - } + boost::char_separator sep("|"); + for (auto const& tok : boost::tokenizer>(path, sep)) { + auto dirname = StandardPaths::DecodePath(tok); + if (!agi::fs::DirectoryExists(dirname)) continue; - wxString fn; - wxFileName script_path(dirname + "/", ""); - bool more = dir.GetFirst(&fn, wxEmptyString, wxDIR_FILES); - while (more) { - script_path.SetName(fn); - wxString fullpath = script_path.GetFullPath(); - if (ScriptFactory::CanHandleScriptFormat(fullpath)) { - Script *s = ScriptFactory::CreateFromFile(fullpath, true); + for (auto filename : agi::fs::DirectoryIterator(dirname, "*.*")) { + Script *s = ScriptFactory::CreateFromFile(dirname/filename, true); + if (s) { scripts.push_back(s); - if (!s->GetLoadedState()) error_count++; + if (!s->GetLoadedState()) ++error_count; } - more = dir.GetNext(&fn); } } @@ -415,39 +387,37 @@ namespace Automation4 { { delete_clear(scripts); - wxString local_scripts = context->ass->GetScriptInfo("Automation Scripts"); + auto local_scripts = context->ass->GetScriptInfo("Automation Scripts"); if (local_scripts.empty()) { ScriptsChanged(); return; } - wxStringTokenizer tok(local_scripts, "|", wxTOKEN_STRTOK); - wxFileName assfn(context->ass->filename); - wxString autobasefn(to_wx(OPT_GET("Path/Automation/Base")->GetString())); - while (tok.HasMoreTokens()) { - wxString trimmed = tok.GetNextToken().Trim(true).Trim(false); - char first_char = trimmed[0]; - trimmed.Remove(0, 1); + auto autobasefn(OPT_GET("Path/Automation/Base")->GetString()); - wxString basepath; + boost::char_separator sep("|"); + for (auto const& cur : boost::tokenizer>(local_scripts, sep)) { + auto trimmed = boost::trim_copy(cur); + char first_char = trimmed[0]; + trimmed.erase(0); + + agi::fs::path basepath; if (first_char == '~') { - basepath = assfn.GetPath(); + basepath = context->ass->filename.parent_path(); } else if (first_char == '$') { basepath = autobasefn; } else if (first_char == '/') { } else { wxLogWarning("Automation Script referenced with unknown location specifier character.\nLocation specifier found: %c\nFilename specified: %s", - first_char, trimmed); + first_char, to_wx(trimmed)); continue; } - wxFileName sfname(trimmed); - sfname.MakeAbsolute(basepath); - if (sfname.FileExists()) { - scripts.push_back(Automation4::ScriptFactory::CreateFromFile(sfname.GetFullPath(), true)); - } + auto sfname = basepath/trimmed; + if (agi::fs::FileExists(sfname)) + scripts.push_back(Automation4::ScriptFactory::CreateFromFile(sfname, true)); else { wxLogWarning("Automation Script referenced could not be found.\nFilename specified: %c%s\nSearched relative to: %s\nResolved filename: %s", - first_char, trimmed, basepath, sfname.GetFullPath()); + first_char, to_wx(trimmed), basepath.wstring(), sfname.wstring()); } } @@ -462,24 +432,23 @@ namespace Automation4 { // 2. Otherwise try making it relative to the ass filename // 3. If step 2 failed, or absolute path is shorter than path relative to ass, use absolute path ("/") // 4. Otherwise, use path relative to ass ("~") - wxString scripts_string; - wxString autobasefn(to_wx(OPT_GET("Path/Automation/Base")->GetString())); + std::string scripts_string; + agi::fs::path autobasefn(OPT_GET("Path/Automation/Base")->GetString()); for (auto script : GetScripts()) { if (!scripts_string.empty()) scripts_string += "|"; - wxString autobase_rel, assfile_rel; - wxString scriptfn(script->GetFilename()); - autobase_rel = MakeRelativePath(scriptfn, autobasefn); - assfile_rel = MakeRelativePath(scriptfn, context->ass->filename); + auto scriptfn(script->GetFilename().string()); + auto autobase_rel = config::path->MakeRelative(scriptfn, autobasefn); + auto assfile_rel = config::path->MakeRelative(scriptfn, "?script"); - if (autobase_rel.size() <= scriptfn.size() && autobase_rel.size() <= assfile_rel.size()) { - scriptfn = "$" + autobase_rel; - } else if (assfile_rel.size() <= scriptfn.size() && assfile_rel.size() <= autobase_rel.size()) { - scriptfn = "~" + assfile_rel; + if (autobase_rel.string().size() <= scriptfn.size() && autobase_rel.string().size() <= assfile_rel.string().size()) { + scriptfn = "$" + autobase_rel.generic_string(); + } else if (assfile_rel.string().size() <= scriptfn.size() && assfile_rel.string().size() <= autobase_rel.string().size()) { + scriptfn = "~" + assfile_rel.generic_string(); } else { - scriptfn = "/" + wxFileName(scriptfn).GetFullPath(wxPATH_UNIX); + scriptfn = "/" + script->GetFilename().generic_string(); } scripts_string += scriptfn; @@ -488,7 +457,7 @@ namespace Automation4 { } // ScriptFactory - ScriptFactory::ScriptFactory(wxString engine_name, wxString filename_pattern) + ScriptFactory::ScriptFactory(std::string const& engine_name, std::string const& filename_pattern) : engine_name(engine_name) , filename_pattern(filename_pattern) { @@ -511,32 +480,23 @@ namespace Automation4 { } } - Script* ScriptFactory::CreateFromFile(wxString const& filename, bool log_errors) + Script* ScriptFactory::CreateFromFile(agi::fs::path const& filename, bool log_errors, bool create_unknown) { for (auto factory : Factories()) { Script *s = factory->Produce(filename); if (s) { if (!s->GetLoadedState() && log_errors) { - wxLogError(_("An Automation script failed to load. File name: '%s', error reported: %s"), filename, s->GetDescription()); + wxLogError(_("An Automation script failed to load. File name: '%s', error reported: %s"), filename.wstring(), s->GetDescription()); } return s; } } if (log_errors) { - wxLogError(_("The file was not recognised as an Automation script: %s"), filename); + wxLogError(_("The file was not recognised as an Automation script: %s"), filename.wstring()); } - - return new UnknownScript(filename); - } - - bool ScriptFactory::CanHandleScriptFormat(wxString const& filename) - { - using std::placeholders::_1; - // Just make this always return true to bitch about unknown script formats in autoload - return any_of(Factories().begin(), Factories().end(), - [&](ScriptFactory *sf) { return filename.Matches(sf->GetFilenamePattern()); }); + return create_unknown ? new UnknownScript(filename) : nullptr; } std::vector& ScriptFactory::Factories() @@ -550,30 +510,24 @@ namespace Automation4 { return Factories(); } - wxString ScriptFactory::GetWildcardStr() + std::string ScriptFactory::GetWildcardStr() { - wxString fnfilter, catchall; + std::string fnfilter, catchall; for (auto fact : Factories()) { if (fact->GetEngineName().empty() || fact->GetFilenamePattern().empty()) continue; - fnfilter = wxString::Format("%s%s scripts (%s)|%s|", fnfilter, fact->GetEngineName(), fact->GetFilenamePattern(), fact->GetFilenamePattern()); + fnfilter += str(boost::format("%s scripts (%s)|%s|") % fact->GetEngineName() % fact->GetFilenamePattern() % fact->GetFilenamePattern()); catchall += fact->GetFilenamePattern() + ";"; } - fnfilter += _("All Files") + " (*.*)|*.*"; + fnfilter += from_wx(_("All Files")) + " (*.*)|*.*"; if (!catchall.empty()) - catchall.RemoveLast(); + catchall.pop_back(); if (Factories().size() > 1) - fnfilter = _("All Supported Formats") + "|" + catchall + "|" + fnfilter; + fnfilter = from_wx(_("All Supported Formats")) + "|" + catchall + "|" + fnfilter; return fnfilter; } - - // UnknownScript - UnknownScript::UnknownScript(wxString const& filename) - : Script(filename) - { - } } diff --git a/aegisub/src/auto4_base.h b/aegisub/src/auto4_base.h index c63c1960a..42cf2dad1 100644 --- a/aegisub/src/auto4_base.h +++ b/aegisub/src/auto4_base.h @@ -34,24 +34,20 @@ #pragma once -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - #include #include -#include +#include #include #include "ass_export_filter.h" +#include "compat.h" + +#include +#include +#include +#include + +#include class AssFile; class AssStyle; @@ -59,8 +55,6 @@ class DialogProgress; class SubtitleFormat; class wxWindow; class wxDialog; -class wxStopWatch; -class wxPathList; namespace agi { struct Context; } namespace cmd { class Command; } @@ -71,21 +65,21 @@ namespace Automation4 { DEFINE_SIMPLE_EXCEPTION_NOINNER(MacroRunError, AutomationError, "automation/macro/generic") // Calculate the extents of a text string given a style - bool CalculateTextExtents(AssStyle *style, wxString const& text, double &width, double &height, double &descent, double &extlead); + bool CalculateTextExtents(AssStyle *style, std::string const& text, double &width, double &height, double &descent, double &extlead); class ScriptDialog; class ExportFilter : public AssExportFilter { - agi::scoped_ptr config_dialog; + std::unique_ptr config_dialog; /// subclasses should implement this, producing a new ScriptDialog virtual ScriptDialog* GenerateConfigDialog(wxWindow *parent, agi::Context *c) = 0; protected: - wxString GetScriptSettingsIdentifier(); + std::string GetScriptSettingsIdentifier(); public: - ExportFilter(wxString const& name, wxString const& description, int priority); + ExportFilter(std::string const& name, std::string const& description, int priority); virtual ~ExportFilter(); wxWindow* GetConfigDialogWindow(wxWindow *parent, agi::Context *c); @@ -106,25 +100,25 @@ namespace Automation4 { /// Serialize the values of the controls in this dialog to a string /// suitable for storage in the subtitle script - virtual wxString Serialise() { return ""; } + virtual std::string Serialise() { return ""; } /// Restore the values of the controls in this dialog from a string /// stored in the subtitle script - virtual void Unserialise(wxString const& serialised) { } + virtual void Unserialise(std::string const& serialised) { } }; class ProgressSink; class BackgroundScriptRunner { - agi::scoped_ptr impl; + std::unique_ptr impl; public: wxWindow *GetParentWindow() const; - wxString GetTitle() const; + std::string GetTitle() const; void Run(std::function task); - BackgroundScriptRunner(wxWindow *parent, wxString const& title); + BackgroundScriptRunner(wxWindow *parent, std::string const& title); ~BackgroundScriptRunner(); }; @@ -155,13 +149,13 @@ namespace Automation4 { }; class Script { - wxString filename; + agi::fs::path filename; protected: /// The automation include path, consisting of the user-specified paths /// along with the script's path - wxPathList include_path; - Script(wxString const& filename); + std::vector include_path; + Script(agi::fs::path const& filename); public: virtual ~Script() { } @@ -170,17 +164,17 @@ namespace Automation4 { virtual void Reload() = 0; /// The script's file name with path - wxString GetFilename() const { return filename; } + agi::fs::path GetFilename() const { return filename; } /// The script's file name without path - wxString GetPrettyFilename() const { return wxFileName(filename).GetFullName(); } + agi::fs::path GetPrettyFilename() const { return filename.filename(); } /// The script's name. Not required to be unique. - virtual wxString GetName() const=0; + virtual std::string GetName() const=0; /// A short description of the script - virtual wxString GetDescription() const=0; + virtual std::string GetDescription() const=0; /// The author of the script - virtual wxString GetAuthor() const=0; + virtual std::string GetAuthor() const=0; /// A version string that should not be used for anything but display - virtual wxString GetVersion() const=0; + virtual std::string GetVersion() const=0; /// Did the script load correctly? virtual bool GetLoadedState() const=0; @@ -237,17 +231,17 @@ namespace Automation4 { /// Manager for scripts in the autoload directory class AutoloadScriptManager : public ScriptManager { - wxString path; + std::string path; public: - AutoloadScriptManager(wxString const& path); + AutoloadScriptManager(std::string const& path); void Reload(); }; /// Both a base class for script factories and a manager of registered /// script factories class ScriptFactory { - wxString engine_name; - wxString filename_pattern; + std::string engine_name; + std::string filename_pattern; /// Load a file, or return nullptr if the file is not in a supported /// format. If the file is in a supported format but is invalid, a @@ -256,35 +250,33 @@ namespace Automation4 { /// /// This is private as it should only ever be called through /// CreateFromFile - virtual Script* Produce(wxString const& filename) const = 0; + virtual Script* Produce(agi::fs::path const& filename) const = 0; static inline std::vector& Factories(); protected: - ScriptFactory(wxString engine_name, wxString filename_pattern); + ScriptFactory(std::string const& engine_name, std::string const& filename_pattern); virtual ~ScriptFactory() { } public: /// Name of this automation engine - const wxString& GetEngineName() const { return engine_name; } + const std::string& GetEngineName() const { return engine_name; } /// Extension which this engine supports - const wxString& GetFilenamePattern() const { return filename_pattern; } + const std::string& GetFilenamePattern() const { return filename_pattern; } /// Register an automation engine. Calling code retains ownership of pointer static void Register(ScriptFactory *factory); /// Unregister and delete an automation engine static void Unregister(ScriptFactory *factory); - /// Is there an automation engine registered which can open the file? - static bool CanHandleScriptFormat(wxString const& filename); /// Get the full wildcard string for all loaded engines - static wxString GetWildcardStr(); + static std::string GetWildcardStr(); /// Load a script from a file /// @param filename Script to load /// @param log_errors Should load errors be displayed? - /// @return Always returns a valid Script, even if no engine could load the file - static Script* CreateFromFile(wxString const& filename, bool log_errors); + /// @param create_unknown Create a placeholder rather than returning nullptr if no script engine supports the file + static Script* CreateFromFile(agi::fs::path const& filename, bool log_errors, bool create_unknown=true); static const std::vector& GetFactories(); }; @@ -293,14 +285,14 @@ namespace Automation4 { /// automation engines class UnknownScript : public Script { public: - UnknownScript(wxString const& filename); + UnknownScript(agi::fs::path const& filename) : Script(filename) { } void Reload() { } - wxString GetName() const { return wxFileName(GetFilename()).GetName(); } - wxString GetDescription() const { return _("File was not recognized as a script"); } - wxString GetAuthor() const { return ""; } - wxString GetVersion() const { return ""; } + std::string GetName() const { return GetFilename().stem().string(); } + std::string GetDescription() const { return from_wx(_("File was not recognized as a script")); } + std::string GetAuthor() const { return ""; } + std::string GetVersion() const { return ""; } bool GetLoadedState() const { return false; } std::vector GetMacros() const { return std::vector(); } diff --git a/aegisub/src/auto4_lua.cpp b/aegisub/src/auto4_lua.cpp index a12b97538..566957524 100644 --- a/aegisub/src/auto4_lua.cpp +++ b/aegisub/src/auto4_lua.cpp @@ -38,24 +38,6 @@ #include "auto4_lua.h" -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - #include "ass_dialogue.h" #include "ass_file.h" #include "ass_style.h" @@ -69,6 +51,28 @@ #include "video_context.h" #include "utils.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + // This must be below the headers above. #ifdef __WINDOWS__ #include "../../contrib/lua51/src/lualib.h" @@ -78,21 +82,34 @@ #endif namespace { - inline void push_value(lua_State *L, lua_CFunction fn) - { + inline void push_value(lua_State *L, lua_CFunction fn) { lua_pushcfunction(L, fn); } - inline void push_value(lua_State *L, int n) - { + inline void push_value(lua_State *L, int n) { lua_pushinteger(L, n); } - inline void push_value(lua_State *L, void *p) - { + inline void push_value(lua_State *L, void *p) { lua_pushlightuserdata(L, p); } + inline void push_value(lua_State *L, agi::fs::path const& p) { + lua_pushstring(L, p.string().c_str()); + } + + inline void push_value(lua_State *L, wxString const& s) { + lua_pushstring(L, s.utf8_str()); + } + + inline void push_value(lua_State *L, std::string const& s) { + lua_pushstring(L, s.c_str()); + } + + inline void push_value(lua_State *L, const char *s) { + lua_pushstring(L, s); + } + template inline void set_field(lua_State *L, const char *name, T value) { @@ -102,20 +119,20 @@ namespace { inline wxString get_wxstring(lua_State *L, int idx) { - return wxString(lua_tostring(L, idx), wxConvUTF8); + return wxString::FromUTF8(lua_tostring(L, idx)); } inline wxString check_wxstring(lua_State *L, int idx) { - return wxString(luaL_checkstring(L, idx), wxConvUTF8); + return wxString::FromUTF8(luaL_checkstring(L, idx)); } - wxString get_global_string(lua_State *L, const char *name) + std::string get_global_string(lua_State *L, const char *name) { lua_getglobal(L, name); - wxString ret; + std::string ret; if (lua_isstring(L, -1)) - ret = get_wxstring(L, -1); + ret = lua_tostring(L, -1); lua_pop(L, 1); return ret; } @@ -123,7 +140,7 @@ namespace { void set_context(lua_State *L, const agi::Context *c) { // Explicit cast is needed to discard the const - lua_pushlightuserdata(L, (void *)c); + push_value(L, (void *)c); lua_setfield(L, LUA_REGISTRYINDEX, "project_context"); } @@ -142,8 +159,8 @@ namespace { int get_file_name(lua_State *L) { const agi::Context *c = get_context(L); - if (c && c->ass->filename.size()) - lua_pushstring(L, wxFileName(c->ass->filename).GetFullName().utf8_str()); + if (c && !c->ass->filename.empty()) + push_value(L, c->ass->filename.filename()); else lua_pushnil(L); return 1; @@ -152,7 +169,7 @@ namespace { int get_translation(lua_State *L) { wxString str(check_wxstring(L, 1)); - lua_pushstring(L, _(str).utf8_str()); + push_value(L, _(str)); return 1; } @@ -171,9 +188,9 @@ namespace { { wxRegEx *re = get_regex(L); if (re->Matches(check_wxstring(L, 2))) - lua_pushinteger(L, re->GetMatchCount()); + push_value(L, re->GetMatchCount()); else - lua_pushinteger(L, 0); + push_value(L, 0); return 1; } @@ -191,8 +208,8 @@ namespace { wxString str(check_wxstring(L, 2)); size_t start, len; get_regex(L)->GetMatch(&start, &len, luaL_checkinteger(L, 3)); - lua_pushinteger(L, utf8_len(str.Left(start)) + 1); - lua_pushinteger(L, utf8_len(str.Left(start + len))); + push_value(L, utf8_len(str.Left(start)) + 1); + push_value(L, utf8_len(str.Left(start + len))); return 2; } @@ -200,8 +217,8 @@ namespace { { wxString str(check_wxstring(L, 3)); int reps = get_regex(L)->Replace(&str, check_wxstring(L, 2), luaL_checkinteger(L, 4)); - lua_pushstring(L, str.utf8_str()); - lua_pushinteger(L, reps); + push_value(L, str); + push_value(L, reps); return 2; } @@ -236,13 +253,13 @@ namespace { int nargs = lua_gettop(L); for (int i = 1; i <= nargs; ++i) { if (!lua_islightuserdata(L, i)) { - lua_pushstring(L, "Flags must follow all non-flag arguments"); + push_value(L, "Flags must follow all non-flag arguments"); return 1; } ret |= (int)(intptr_t)lua_touserdata(L, i); } - lua_pushinteger(L, ret); + push_value(L, ret); return 1; } @@ -277,17 +294,17 @@ namespace { int clipboard_get(lua_State *L) { - wxString data = GetClipboard(); - if (!data) + std::string data = GetClipboard(); + if (data.empty()) lua_pushnil(L); else - lua_pushstring(L, data.utf8_str()); + push_value(L, data); return 1; } int clipboard_set(lua_State *L) { - wxString str(check_wxstring(L, 1)); + std::string str(luaL_checkstring(L, 1)); bool succeeded = false; @@ -300,7 +317,7 @@ namespace { wxClipboard *theCB = wxTheClipboard; #endif if (theCB->Open()) { - succeeded = theCB->SetData(new wxTextDataObject(str)); + succeeded = theCB->SetData(new wxTextDataObject(to_wx(str))); theCB->Close(); theCB->Flush(); } @@ -338,7 +355,7 @@ namespace { LOG_D("automation/lua/stackdump") << "--- dumping lua stack..."; for (int i = top; i > 0; i--) { lua_pushvalue(L, i); - wxString type(lua_typename(L, lua_type(L, -1)), wxConvUTF8); + std::string type(lua_typename(L, lua_type(L, -1))); if (lua_isstring(L, i)) { LOG_D("automation/lua/stackdump") << type << ": " << lua_tostring(L, -1); } else { @@ -361,7 +378,7 @@ namespace { namespace Automation4 { // LuaScript - LuaScript::LuaScript(wxString const& filename) + LuaScript::LuaScript(agi::fs::path const& filename) : Script(filename) , L(0) { @@ -383,13 +400,13 @@ namespace Automation4 { LuaStackcheck _stackcheck(L); // register standard libs - lua_pushcfunction(L, luaopen_base); lua_call(L, 0, 0); - lua_pushcfunction(L, luaopen_package); lua_call(L, 0, 0); - lua_pushcfunction(L, luaopen_string); lua_call(L, 0, 0); - lua_pushcfunction(L, luaopen_table); lua_call(L, 0, 0); - lua_pushcfunction(L, luaopen_math); lua_call(L, 0, 0); - lua_pushcfunction(L, luaopen_io); lua_call(L, 0, 0); - lua_pushcfunction(L, luaopen_os); lua_call(L, 0, 0); + push_value(L, luaopen_base); lua_call(L, 0, 0); + push_value(L, luaopen_package); lua_call(L, 0, 0); + push_value(L, luaopen_string); lua_call(L, 0, 0); + push_value(L, luaopen_table); lua_call(L, 0, 0); + push_value(L, luaopen_math); lua_call(L, 0, 0); + push_value(L, luaopen_io); lua_call(L, 0, 0); + push_value(L, luaopen_os); lua_call(L, 0, 0); _stackcheck.check_stack(0); // dofile and loadfile are replaced with include @@ -397,18 +414,17 @@ namespace Automation4 { lua_setglobal(L, "dofile"); lua_pushnil(L); lua_setglobal(L, "loadfile"); - lua_pushcfunction(L, LuaInclude); + push_value(L, LuaInclude); lua_setglobal(L, "include"); // add include_path to the module load path lua_getglobal(L, "package"); - lua_pushstring(L, "path"); - lua_pushstring(L, "path"); + push_value(L, "path"); + push_value(L, "path"); lua_gettable(L, -3); - for (wxString const& path : include_path) { - wxCharBuffer p = path.utf8_str(); - lua_pushfstring(L, ";%s/?.lua;%s/?/init.lua", p.data(), p.data()); + for (auto const& path : include_path) { + lua_pushfstring(L, ";%s/?.lua;%s/?/init.lua", path.string().c_str(), path.string().c_str()); lua_concat(L, 2); } @@ -416,7 +432,7 @@ namespace Automation4 { // Replace the default lua module loader with our unicode compatible one lua_getfield(L, -1, "loaders"); - lua_pushcfunction(L, LuaModuleLoader); + push_value(L, LuaModuleLoader); lua_rawseti(L, -2, 2); lua_pop(L, 2); _stackcheck.check_stack(0); @@ -424,12 +440,12 @@ namespace Automation4 { // prepare stuff in the registry // store the script's filename - lua_pushstring(L, wxFileName(GetFilename()).GetName().utf8_str().data()); + push_value(L, GetFilename().stem()); lua_setfield(L, LUA_REGISTRYINDEX, "filename"); _stackcheck.check_stack(0); // reference to the script object - lua_pushlightuserdata(L, this); + push_value(L, this); lua_setfield(L, LUA_REGISTRYINDEX, "aegisub"); _stackcheck.check_stack(0); @@ -458,10 +474,10 @@ namespace Automation4 { // load user script LuaScriptReader script_reader(GetFilename()); - if (lua_load(L, script_reader.reader_func, &script_reader, GetPrettyFilename().utf8_str())) { - wxString err = wxString::Format("Error loading Lua script \"%s\":\n\n%s", GetPrettyFilename(), get_wxstring(L, -1)); + if (lua_load(L, script_reader.reader_func, &script_reader, GetPrettyFilename().string().c_str())) { + std::string err = str(boost::format("Error loading Lua script \"%s\":\n\n%s") % GetPrettyFilename().string() % lua_tostring(L, -1)); lua_pop(L, 1); - throw ScriptLoadError(from_wx(err)); + throw ScriptLoadError(err); } _stackcheck.check_stack(1); @@ -470,9 +486,9 @@ namespace Automation4 { // don't thread this, as there's no point in it and it seems to break on wx 2.8.3, for some reason if (lua_pcall(L, 0, 0, 0)) { // error occurred, assumed to be on top of Lua stack - wxString err = wxString::Format("Error initialising Lua script \"%s\":\n\n%s", GetPrettyFilename(), get_wxstring(L, -1)); + std::string err = str(boost::format("Error initialising Lua script \"%s\":\n\n%s") % GetPrettyFilename().string() % lua_tostring(L, -1)); lua_pop(L, 1); - throw ScriptLoadError(from_wx(err)); + throw ScriptLoadError(err); } _stackcheck.check_stack(0); @@ -488,7 +504,7 @@ namespace Automation4 { version = get_global_string(L, "script_version"); if (name.empty()) - name = GetPrettyFilename(); + name = GetPrettyFilename().string(); lua_pop(L, 1); // if we got this far, the script should be ready @@ -497,8 +513,8 @@ namespace Automation4 { } catch (agi::Exception const& e) { Destroy(); - name = GetPrettyFilename(); - description = to_wx(e.GetChainedMessage()); + name = GetPrettyFilename().string(); + description = e.GetChainedMessage(); } } @@ -529,7 +545,7 @@ namespace Automation4 { if (macro->name() == command->name()) { luaL_error(L, "A macro named '%s' is already defined in script '%s'", - command->StrDisplay(0).utf8_str().data(), name.utf8_str().data()); + command->StrDisplay(0).utf8_str().data(), name.c_str()); } } macros.push_back(command); @@ -566,13 +582,13 @@ namespace Automation4 { return luaL_error(L, "Not a style entry"); double width, height, descent, extlead; - if (!CalculateTextExtents(st, check_wxstring(L, 2), width, height, descent, extlead)) + if (!CalculateTextExtents(st, luaL_checkstring(L, 2), width, height, descent, extlead)) return luaL_error(L, "Some internal error occurred calculating text_extents"); - lua_pushnumber(L, width); - lua_pushnumber(L, height); - lua_pushnumber(L, descent); - lua_pushnumber(L, extlead); + push_value(L, width); + push_value(L, height); + push_value(L, descent); + push_value(L, extlead); return 4; } @@ -582,35 +598,33 @@ namespace Automation4 { int LuaScript::LuaModuleLoader(lua_State *L) { int pretop = lua_gettop(L); - wxString module(get_wxstring(L, -1)); - module.Replace(".", LUA_DIRSEP); + std::string module(lua_tostring(L, -1)); + boost::replace_all(module, ".", LUA_DIRSEP); // Get the lua package include path (which the user may have modified) lua_getglobal(L, "package"); - lua_pushstring(L, "path"); + push_value(L, "path"); lua_gettable(L, -2); - wxString package_paths(get_wxstring(L, -1)); + std::string package_paths(lua_tostring(L, -1)); lua_pop(L, 2); - wxStringTokenizer toker(package_paths, ";", wxTOKEN_STRTOK); - while (toker.HasMoreTokens()) { - wxString filename = toker.GetNextToken(); - filename.Replace("?", module); + boost::char_separator sep(";"); + for (auto filename : boost::tokenizer>(package_paths, sep)) { + boost::replace_all(filename, "?", module); try { LuaScriptReader script_reader(filename); - if (lua_load(L, script_reader.reader_func, &script_reader, filename.utf8_str())) { - return luaL_error(L, "Error loading Lua module \"%s\":\n\n%s", filename.utf8_str().data(), lua_tostring(L, -1)); - } + if (lua_load(L, script_reader.reader_func, &script_reader, filename.c_str())) + return luaL_error(L, "Error loading Lua module \"%s\":\n\n%s", filename.c_str(), lua_tostring(L, -1)); break; } - catch (agi::FileNotFoundError const&) { + catch (agi::fs::FileNotFound const&) { // Not an error so swallow and continue on } - catch (agi::acs::NotAFile const&) { + catch (agi::fs::NotAFile const&) { // Not an error so swallow and continue on } catch (agi::Exception const& e) { - return luaL_error(L, "Error loading Lua module \"%s\":\n\n%s", filename.utf8_str().data(), e.GetChainedMessage().c_str()); + return luaL_error(L, "Error loading Lua module \"%s\":\n\n%s", filename.c_str(), e.GetChainedMessage().c_str()); } } @@ -619,27 +633,28 @@ namespace Automation4 { int LuaScript::LuaInclude(lua_State *L) { - LuaScript *s = GetScriptObject(L); + const LuaScript *s = GetScriptObject(L); - wxString fnames(check_wxstring(L, 1)); + const std::string filename(luaL_checkstring(L, 1)); + agi::fs::path filepath; - wxFileName fname(fnames); - if (fname.GetDirCount() == 0) { - // filename only - fname = s->include_path.FindAbsoluteValidPath(fnames); - } else if (fname.IsRelative()) { - // relative path - wxFileName sfname(s->GetFilename()); - fname.MakeAbsolute(sfname.GetPath(true)); - } else { - // absolute path, do nothing + // Relative or absolute path + if (!boost::all(filename, !boost::is_any_of("/\\"))) + filepath = s->GetFilename().parent_path()/filename; + else { // Plain filename + for (auto const& dir : s->include_path) { + filepath = dir/filename; + if (agi::fs::FileExists(filepath)) + break; + } } - if (!fname.IsOk() || !fname.FileExists()) - return luaL_error(L, "Lua include not found: %s", fnames.utf8_str().data()); - LuaScriptReader script_reader(fname.GetFullPath()); - if (lua_load(L, script_reader.reader_func, &script_reader, fname.GetFullName().utf8_str())) - return luaL_error(L, "Error loading Lua include \"%s\":\n\n%s", fname.GetFullPath().utf8_str().data(), lua_tostring(L, -1)); + if (!agi::fs::FileExists(filepath)) + return luaL_error(L, "Lua include not found: %s", filename.c_str()); + + LuaScriptReader script_reader(filepath); + if (lua_load(L, script_reader.reader_func, &script_reader, filename.c_str())) + return luaL_error(L, "Error loading Lua include \"%s\":\n\n%s", filename.c_str(), lua_tostring(L, -1)); int pretop = lua_gettop(L) - 1; // don't count the function value itself lua_call(L, 0, LUA_MULTRET); @@ -652,7 +667,7 @@ namespace Automation4 { int ms = lua_tointeger(L, -1); lua_pop(L, 1); if (c && c->videoController->TimecodesLoaded()) - lua_pushnumber(L, c->videoController->FrameAtTime(ms, agi::vfr::START)); + push_value(L, c->videoController->FrameAtTime(ms, agi::vfr::START)); else lua_pushnil(L); @@ -665,7 +680,7 @@ namespace Automation4 { int frame = lua_tointeger(L, -1); lua_pop(L, 1); if (c && c->videoController->TimecodesLoaded()) - lua_pushnumber(L, c->videoController->TimeAtFrame(frame, agi::vfr::START)); + push_value(L, c->videoController->TimeAtFrame(frame, agi::vfr::START)); else lua_pushnil(L); return 1; @@ -675,10 +690,10 @@ namespace Automation4 { { const agi::Context *c = get_context(L); if (c && c->videoController->IsLoaded()) { - lua_pushnumber(L, c->videoController->GetWidth()); - lua_pushnumber(L, c->videoController->GetHeight()); - lua_pushnumber(L, c->videoController->GetAspectRatioValue()); - lua_pushnumber(L, c->videoController->GetAspectRatioType()); + push_value(L, c->videoController->GetWidth()); + push_value(L, c->videoController->GetHeight()); + push_value(L, c->videoController->GetAspectRatioValue()); + push_value(L, (int)c->videoController->GetAspectRatioType()); return 4; } else { @@ -699,7 +714,7 @@ namespace Automation4 { lua_newtable(L); for (size_t i = 0; i < kf.size(); ++i) { - lua_pushinteger(L, kf[i]); + push_value(L, kf[i]); lua_rawseti(L, -2, i); } @@ -708,9 +723,9 @@ namespace Automation4 { int LuaScript::LuaDecodePath(lua_State *L) { - wxString path = check_wxstring(L, 1); + std::string path = luaL_checkstring(L, 1); lua_pop(L, 1); - lua_pushstring(L, StandardPaths::DecodePath(path).utf8_str()); + push_value(L, StandardPaths::DecodePath(path)); return 1; } @@ -720,7 +735,7 @@ namespace Automation4 { return lua_error(L); } - void LuaThreadedCall(lua_State *L, int nargs, int nresults, wxString const& title, wxWindow *parent, bool can_open_config) + void LuaThreadedCall(lua_State *L, int nargs, int nresults, std::string const& title, wxWindow *parent, bool can_open_config) { bool failed = false; BackgroundScriptRunner bsr(parent, title); @@ -765,7 +780,7 @@ namespace Automation4 { // get this feature's function pointers lua_rawgeti(L, LUA_REGISTRYINDEX, myid); // get pointer for validation function - lua_pushstring(L, function); + push_value(L, function); lua_rawget(L, -2); // remove the function table lua_remove(L, -2); @@ -786,7 +801,7 @@ namespace Automation4 { , cmd_type(cmd::COMMAND_NORMAL) { lua_getfield(L, LUA_REGISTRYINDEX, "filename"); - cmd_name = from_wx(wxString::Format("automation/lua/%s/%s", get_wxstring(L, -1), check_wxstring(L, 1))); + cmd_name = str(boost::format("automation/lua/%s/%s") % lua_tostring(L, -1) % luaL_checkstring(L, 1)); if (!lua_isfunction(L, 3)) luaL_error(L, "The macro processing function must be a function"); @@ -801,17 +816,17 @@ namespace Automation4 { lua_newtable(L); // store processing function - lua_pushstring(L, "run"); + push_value(L, "run"); lua_pushvalue(L, 3); lua_rawset(L, -3); // store validation function - lua_pushstring(L, "validate"); + push_value(L, "validate"); lua_pushvalue(L, 4); lua_rawset(L, -3); // store active function - lua_pushstring(L, "isactive"); + push_value(L, "isactive"); lua_pushvalue(L, 5); lua_rawset(L, -3); @@ -844,7 +859,7 @@ namespace Automation4 { if (diag == active_line) active_idx = row; if (sel.count(diag)) { - lua_pushinteger(L, row); + push_value(L, row); lua_rawseti(L, -2, idx++); } } @@ -860,7 +875,7 @@ namespace Automation4 { GetFeatureFunction("validate"); LuaAssFile *subsobj = new LuaAssFile(L, c->ass); - lua_pushinteger(L, transform_selection(L, c)); + push_value(L, transform_selection(L, c)); int err = lua_pcall(L, 3, 2, 0); @@ -893,10 +908,10 @@ namespace Automation4 { GetFeatureFunction("run"); LuaAssFile *subsobj = new LuaAssFile(L, c->ass, true, true); - lua_pushinteger(L, transform_selection(L, c)); + push_value(L, transform_selection(L, c)); try { - LuaThreadedCall(L, 3, 2, StrDisplay(c), c->parent, true); + LuaThreadedCall(L, 3, 2, from_wx(StrDisplay(c)), c->parent, true); subsobj->ProcessingComplete(StrDisplay(c)); @@ -971,7 +986,7 @@ namespace Automation4 { GetFeatureFunction("isactive"); LuaAssFile *subsobj = new LuaAssFile(L, c->ass); - lua_pushinteger(L, transform_selection(L, c)); + push_value(L, transform_selection(L, c)); int err = lua_pcall(L, 3, 1, 0); subsobj->ProcessingComplete(); @@ -991,7 +1006,7 @@ namespace Automation4 { // LuaFeatureFilter LuaExportFilter::LuaExportFilter(lua_State *L) - : ExportFilter(check_wxstring(L, 1), get_wxstring(L, 2), lua_tointeger(L, 3)) + : ExportFilter(luaL_checkstring(L, 1), lua_tostring(L, 2), lua_tointeger(L, 3)) , LuaFeature(L) { if (!lua_isfunction(L, 4)) @@ -1001,12 +1016,12 @@ namespace Automation4 { lua_newtable(L); // store processing function - lua_pushstring(L, "run"); + push_value(L, "run"); lua_pushvalue(L, 4); lua_rawset(L, -3); // store config function - lua_pushstring(L, "config"); + push_value(L, "config"); lua_pushvalue(L, 5); has_config = lua_isfunction(L, -1); lua_rawset(L, -3); @@ -1096,15 +1111,14 @@ namespace Automation4 { Register(this); } - Script* LuaScriptFactory::Produce(const wxString &filename) const + Script* LuaScriptFactory::Produce(agi::fs::path const& filename) const { // Just check if file extension is .lua // Reject anything else - if (filename.Right(4).Lower() == ".lua") { + if (agi::fs::HasExtension(filename, "lua")) return new LuaScript(filename); - } else { + else return 0; - } } } diff --git a/aegisub/src/auto4_lua.h b/aegisub/src/auto4_lua.h index a53434a6a..7f79157d2 100644 --- a/aegisub/src/auto4_lua.h +++ b/aegisub/src/auto4_lua.h @@ -148,10 +148,10 @@ namespace Automation4 { class LuaDialogControl { public: /// Name of this control in the output table - wxString name; + std::string name; /// Tooltip of this control - wxString hint; + std::string hint; int x, y, width, height; @@ -170,10 +170,10 @@ namespace Automation4 { /// Serialize the control's current value so that it can be stored /// in the script - virtual wxString SerialiseValue() const { return ""; } + virtual std::string SerialiseValue() const { return ""; } /// Restore the control's value from a saved value in the script - virtual void UnserialiseValue(const wxString &serialised) { } + virtual void UnserialiseValue(const std::string &serialised) { } LuaDialogControl(lua_State *L); @@ -186,7 +186,7 @@ namespace Automation4 { /// Controls in this dialog std::vector controls; /// The names of buttons in this dialog if non-default ones were used - std::vector buttons; + std::vector buttons; /// Does the dialog contain any buttons bool use_buttons; @@ -208,8 +208,8 @@ namespace Automation4 { // ScriptDialog implementation wxWindow* CreateWindow(wxWindow *parent); - wxString Serialise(); - void Unserialise(const wxString &serialised); + std::string Serialise(); + void Unserialise(const std::string &serialised); }; class LuaFeature { @@ -233,7 +233,7 @@ namespace Automation4 { /// @param parent Parent window for the progress dialog /// @param can_open_config Can the function open its own dialogs? /// @throws agi::UserCancelException if the function fails to run to completion (either due to cancelling or errors) - void LuaThreadedCall(lua_State *L, int nargs, int nresults, wxString const& title, wxWindow *parent, bool can_open_config); + void LuaThreadedCall(lua_State *L, int nargs, int nresults, std::string const& title, wxWindow *parent, bool can_open_config); class LuaCommand : public cmd::Command, private LuaFeature { std::string cmd_name; @@ -278,10 +278,10 @@ namespace Automation4 { class LuaScript : public Script { lua_State *L; - wxString name; - wxString description; - wxString author; - wxString version; + std::string name; + std::string description; + std::string author; + std::string version; std::vector macros; std::vector filters; @@ -302,7 +302,7 @@ namespace Automation4 { static int LuaCancel(lua_State *L); public: - LuaScript(const wxString &filename); + LuaScript(agi::fs::path const& filename); ~LuaScript(); void RegisterCommand(LuaCommand *command); @@ -314,10 +314,10 @@ namespace Automation4 { // Script implementation void Reload(); - wxString GetName() const { return name; } - wxString GetDescription() const { return description; } - wxString GetAuthor() const { return author; } - wxString GetVersion() const { return version; } + std::string GetName() const { return name; } + std::string GetDescription() const { return description; } + std::string GetAuthor() const { return author; } + std::string GetVersion() const { return version; } bool GetLoadedState() const { return L != 0; } std::vector GetMacros() const { return macros; } diff --git a/aegisub/src/auto4_lua_assfile.cpp b/aegisub/src/auto4_lua_assfile.cpp index d73823bdc..07e7b87af 100644 --- a/aegisub/src/auto4_lua_assfile.cpp +++ b/aegisub/src/auto4_lua_assfile.cpp @@ -35,27 +35,25 @@ #include "config.h" #ifdef WITH_AUTO4_LUA - -#include -#include - -#include -#include - -#include - -#include -#include -#include +#include "auto4_lua.h" #include "ass_dialogue.h" #include "ass_info.h" #include "ass_file.h" #include "ass_karaoke.h" #include "ass_style.h" -#include "auto4_lua.h" #include "utils.h" +#include +#include +#include + +#include +#include +#include +#include +#include + // This must be below the headers above. #ifdef __WINDOWS__ #include "../../contrib/lua51/src/lualib.h" @@ -65,10 +63,6 @@ #endif namespace { - void push_value(lua_State *L, wxString const& value) { - lua_pushstring(L, value.utf8_str()); - } - void push_value(lua_State *L, std::string const& value) { lua_pushstring(L, value.c_str()); } @@ -108,16 +102,6 @@ namespace { return BadField(std::string("Invalid or missing field '") + name + "' in '" + line_clasee + "' class subtitle line (expected " + expected_type + ")"); } - wxString get_wxstring_field(lua_State *L, const char *name, const char *line_class) - { - lua_getfield(L, -1, name); - if (!lua_isstring(L, -1)) - throw bad_field("string", name, line_class); - wxString ret(lua_tostring(L, -1), wxConvUTF8); - lua_pop(L, 1); - return ret; - } - std::string get_string_field(lua_State *L, const char *name, const char *line_class) { lua_getfield(L, -1, name); @@ -202,10 +186,8 @@ namespace Automation4 { { lua_newtable(L); - wxString raw(e->GetEntryData()); - set_field(L, "section", e->GroupHeader()); - set_field(L, "raw", raw); + set_field(L, "raw", e->GetEntryData()); if (AssInfo *info = dynamic_cast(e)) { set_field(L, "key", info->Key()); @@ -291,18 +273,15 @@ namespace Automation4 { if (!lua_isstring(L, -1)) luaL_error(L, "Table lacks 'class' field, can't convert to AssEntry"); - wxString lclass(lua_tostring(L, -1), wxConvUTF8); - lclass.MakeLower(); + std::string lclass(lua_tostring(L, -1)); + boost::to_lower(lclass); lua_pop(L, 1); AssEntry *result = 0; try { - wxString section = get_wxstring_field(L, "section", "common"); - - if (lclass == "info") { - result = new AssInfo(get_wxstring_field(L, "key", "info"), get_wxstring_field(L, "value", "info")); - } + if (lclass == "info") + result = new AssInfo(get_string_field(L, "key", "info"), get_string_field(L, "value", "info")); else if (lclass == "style") { AssStyle *sty = new AssStyle; result = sty; @@ -339,16 +318,16 @@ namespace Automation4 { dia->Layer = get_int_field(L, "layer", "dialogue"); dia->Start = get_int_field(L, "start_time", "dialogue"); dia->End = get_int_field(L, "end_time", "dialogue"); - dia->Style = get_wxstring_field(L, "style", "dialogue"); - dia->Actor = get_wxstring_field(L, "actor", "dialogue"); + dia->Style = get_string_field(L, "style", "dialogue"); + dia->Actor = get_string_field(L, "actor", "dialogue"); dia->Margin[0] = get_int_field(L, "margin_l", "dialogue"); dia->Margin[1] = get_int_field(L, "margin_r", "dialogue"); dia->Margin[2] = get_int_field(L, "margin_t", "dialogue"); - dia->Effect = get_wxstring_field(L, "effect", "dialogue"); - dia->Text = get_wxstring_field(L, "text", "dialogue"); + dia->Effect = get_string_field(L, "effect", "dialogue"); + dia->Text = get_string_field(L, "text", "dialogue"); } else { - luaL_error(L, "Found line with unknown class: %s", lclass.utf8_str().data()); + luaL_error(L, "Found line with unknown class: %s", lclass.c_str()); return 0; } @@ -620,7 +599,7 @@ namespace Automation4 { PendingCommit& back = pending_commits.back(); back.modification_type = modification_type; - back.mesage = wxString(luaL_checkstring(L, 1), wxConvUTF8); + back.mesage = wxString::FromUTF8(luaL_checkstring(L, 1)); back.lines = lines; modification_type = 0; } diff --git a/aegisub/src/auto4_lua_dialog.cpp b/aegisub/src/auto4_lua_dialog.cpp index 4a7de31f1..da383f4fa 100644 --- a/aegisub/src/auto4_lua_dialog.cpp +++ b/aegisub/src/auto4_lua_dialog.cpp @@ -32,37 +32,37 @@ /// @ingroup scripting /// - #include "config.h" #ifdef WITH_AUTO4_LUA #include "auto4_lua.h" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - #include "ass_style.h" #include "colour_button.h" #include "compat.h" #include "string_codec.h" #include "utils.h" +#include "validators.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // These must be after the headers above. #ifdef __WINDOWS__ @@ -73,51 +73,44 @@ #endif namespace { - inline void get_if_right_type(lua_State *L, wxString &def) - { + inline void get_if_right_type(lua_State *L, std::string &def) { if (lua_isstring(L, -1)) - def = wxString(lua_tostring(L, -1), wxConvUTF8); + def = lua_tostring(L, -1); } - inline void get_if_right_type(lua_State *L, double &def) - { + inline void get_if_right_type(lua_State *L, double &def) { if (lua_isnumber(L, -1)) def = lua_tonumber(L, -1); } - inline void get_if_right_type(lua_State *L, int &def) - { + inline void get_if_right_type(lua_State *L, int &def) { if (lua_isnumber(L, -1)) def = lua_tointeger(L, -1); } - inline void get_if_right_type(lua_State *L, bool &def) - { + inline void get_if_right_type(lua_State *L, bool &def) { if (lua_isboolean(L, -1)) def = !!lua_toboolean(L, -1); } template - T get_field(lua_State *L, const char *name, T def) - { + T get_field(lua_State *L, const char *name, T def) { lua_getfield(L, -1, name); get_if_right_type(L, def); lua_pop(L, 1); return def; } - inline wxString get_field(lua_State *L, const char *name) - { - return get_field(L, name, wxString()); + inline std::string get_field(lua_State *L, const char *name) { + return get_field(L, name, std::string()); } template - void read_string_array(lua_State *L, T &cont) - { + void read_string_array(lua_State *L, T &cont) { lua_pushnil(L); while (lua_next(L, -2)) { if (lua_isstring(L, -1)) - cont.push_back(wxString(lua_tostring(L, -1), wxConvUTF8)); + cont.push_back(lua_tostring(L, -1)); lua_pop(L, 1); } lua_pop(L, 1); @@ -135,29 +128,23 @@ namespace Automation4 { , width(get_field(L, "width", 1)) , height(get_field(L, "height", 1)) { - LOG_D("automation/lua/dialog") << "created control: '" << from_wx(name) << "', (" << x << "," << y << ")(" << width << "," << height << "), "<< from_wx(hint); + LOG_D("automation/lua/dialog") << "created control: '" << name << "', (" << x << "," << y << ")(" << width << "," << height << "), " << hint; } namespace LuaControl { /// A static text label class Label : public LuaDialogControl { - wxString label; + std::string label; public: - Label(lua_State *L) - : LuaDialogControl(L) - , label(get_field(L, "label")) - { - } + Label(lua_State *L) : LuaDialogControl(L), label(get_field(L, "label")) { } - wxControl *Create(wxWindow *parent) - { - return new wxStaticText(parent, -1, label); + wxControl *Create(wxWindow *parent) { + return new wxStaticText(parent, -1, to_wx(label)); } int GetSizerFlags() const { return wxALIGN_CENTRE_VERTICAL | wxALIGN_LEFT; } - void LuaReadBack(lua_State *L) - { + void LuaReadBack(lua_State *L) { // Label doesn't produce output, so let it be nil lua_pushnil(L); } @@ -166,10 +153,10 @@ namespace Automation4 { /// A single-line text edit control class Edit : public LuaDialogControl { protected: - wxString text; + std::string text; wxTextCtrl *cw; - public: + public: Edit(lua_State *L) : LuaDialogControl(L) , text(get_field(L, "value")) @@ -182,49 +169,39 @@ namespace Automation4 { } bool CanSerialiseValue() const { return true; } + std::string SerialiseValue() const { return inline_string_encode(text); } + void UnserialiseValue(const std::string &serialised) { text = inline_string_decode(serialised); } - wxString SerialiseValue() const - { - return inline_string_encode(text); - } - - void UnserialiseValue(const wxString &serialised) - { - text = inline_string_decode(serialised); - } - - wxControl *Create(wxWindow *parent) - { - cw = new wxTextCtrl(parent, -1, text); - cw->SetValidator(wxGenericValidator(&text)); - cw->SetToolTip(hint); + wxControl *Create(wxWindow *parent) { + cw = new wxTextCtrl(parent, -1, to_wx(text)); + cw->SetValidator(StringBinder(&text)); + cw->SetToolTip(to_wx(hint)); return cw; } - void LuaReadBack(lua_State *L) - { - lua_pushstring(L, text.utf8_str()); + void LuaReadBack(lua_State *L) { + lua_pushstring(L, text.c_str()); } }; /// A color-picker button class Color : public LuaDialogControl { - wxString text; + std::string text; bool alpha; struct ColorValidator : public wxValidator { - wxString *text; - ColorValidator(wxString *text) : text(text) { } + std::string *text; + ColorValidator(std::string *text) : text(text) { } wxValidator *Clone() const { return new ColorValidator(text); } bool Validate(wxWindow*) { return true; } bool TransferToWindow() { return true; } - bool TransferFromWindow() - { - *text = to_wx(static_cast(GetWindow())->GetColor().GetHexFormatted()); + bool TransferFromWindow() { + *text = static_cast(GetWindow())->GetColor().GetHexFormatted(); return true; } }; + public: Color(lua_State *L, bool alpha) : LuaDialogControl(L) @@ -234,45 +211,31 @@ namespace Automation4 { } bool CanSerialiseValue() const { return true; } + std::string SerialiseValue() const { return inline_string_encode(text); } + void UnserialiseValue(const std::string &serialised) { text = inline_string_decode(serialised); } - wxString SerialiseValue() const - { - return inline_string_encode(text); - } - - void UnserialiseValue(const wxString &serialised) - { - text = inline_string_decode(serialised); - } - - wxControl *Create(wxWindow *parent) - { - agi::Color colour(from_wx(text)); + wxControl *Create(wxWindow *parent) { + agi::Color colour(text); wxControl *cw = new ColourButton(parent, wxSize(50*width,10*height), alpha, colour, ColorValidator(&text)); - cw->SetToolTip(hint); + cw->SetToolTip(to_wx(hint)); return cw; } - void LuaReadBack(lua_State *L) - { - lua_pushstring(L, text.utf8_str()); + void LuaReadBack(lua_State *L) { + lua_pushstring(L, text.c_str()); } }; /// A multiline text edit control class Textbox : public Edit { public: - Textbox(lua_State *L) - : Edit(L) - { - } + Textbox(lua_State *L) : Edit(L) { } // Same serialisation interface as single-line edit - wxControl *Create(wxWindow *parent) - { - cw = new wxTextCtrl(parent, -1, text, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE, wxGenericValidator(&text)); + wxControl *Create(wxWindow *parent) { + cw = new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE, StringBinder(&text)); cw->SetMinSize(wxSize(0, 30)); - cw->SetToolTip(hint); + cw->SetToolTip(to_wx(hint)); return cw; } }; @@ -283,8 +246,8 @@ namespace Automation4 { wxSpinCtrl *cw; int value; int min, max; - public: + public: IntEdit(lua_State *L) : Edit(L) , cw(0) @@ -299,34 +262,21 @@ namespace Automation4 { } bool CanSerialiseValue() const { return true; } + std::string SerialiseValue() const { return std::to_string(value); } + void UnserialiseValue(const std::string &serialised) { value = atoi(serialised.c_str()); } - wxString SerialiseValue() const - { - return wxString::Format("%d", value); - } - - void UnserialiseValue(const wxString &serialised) - { - long tmp; - if (serialised.ToLong(&tmp)) - value = tmp; - } - - wxControl *Create(wxWindow *parent) - { - cw = new wxSpinCtrl(parent, -1, wxString::Format("%d", value), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, value); + wxControl *Create(wxWindow *parent) { + cw = new wxSpinCtrl(parent, -1, "", wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, value); cw->SetValidator(wxGenericValidator(&value)); - cw->SetToolTip(hint); + cw->SetToolTip(to_wx(hint)); return cw; } - void LuaReadBack(lua_State *L) - { + void LuaReadBack(lua_State *L) { lua_pushinteger(L, value); } }; - // Float only edit class FloatEdit : public Edit { double value; @@ -342,8 +292,7 @@ namespace Automation4 { bool Validate(wxWindow*) { return true; } bool TransferToWindow() { return true; } - bool TransferFromWindow() - { + bool TransferFromWindow() { *value = static_cast(GetWindow())->GetValue(); return true; } @@ -365,48 +314,34 @@ namespace Automation4 { } bool CanSerialiseValue() const { return true; } + std::string SerialiseValue() const { return std::to_string(value); } + void UnserialiseValue(const std::string &serialised) { value = atof(serialised.c_str()); } - wxString SerialiseValue() const - { - return wxString::Format("%g", value); - } - - void UnserialiseValue(const wxString &serialised) - { - double newval; - if (serialised.ToDouble(&newval)) - value = newval; - } - - wxControl *Create(wxWindow *parent) - { + wxControl *Create(wxWindow *parent) { if (step > 0) { - scd = new wxSpinCtrlDouble(parent, -1, - wxString::Format("%g", value), wxDefaultPosition, - wxDefaultSize, wxSP_ARROW_KEYS, min, max, value, step); + scd = new wxSpinCtrlDouble(parent, -1, "", wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, value, step); scd->SetValidator(DoubleValidator(&value)); - scd->SetToolTip(hint); + scd->SetToolTip(to_wx(hint)); return scd; } wxFloatingPointValidator val(4, &value, wxNUM_VAL_NO_TRAILING_ZEROES); val.SetRange(min, max); - cw = new wxTextCtrl(parent, -1, SerialiseValue(), wxDefaultPosition, wxDefaultSize, 0, val); - cw->SetToolTip(hint); + cw = new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxDefaultSize, 0, val); + cw->SetToolTip(to_wx(hint)); return cw; } - void LuaReadBack(lua_State *L) - { + void LuaReadBack(lua_State *L) { lua_pushnumber(L, value); } }; /// A dropdown list class Dropdown : public LuaDialogControl { - wxArrayString items; - wxString value; + std::vector items; + std::string value; wxComboBox *cw; public: @@ -420,36 +355,26 @@ namespace Automation4 { } bool CanSerialiseValue() const { return true; } + std::string SerialiseValue() const { return inline_string_encode(value); } + void UnserialiseValue(const std::string &serialised) { value = inline_string_decode(serialised); } - wxString SerialiseValue() const - { - return inline_string_encode(value); - } - - void UnserialiseValue(const wxString &serialised) - { - value = inline_string_decode(serialised); - } - - wxControl *Create(wxWindow *parent) - { - cw = new wxComboBox(parent, -1, value, wxDefaultPosition, wxDefaultSize, items, wxCB_READONLY, wxGenericValidator(&value)); - cw->SetToolTip(hint); + wxControl *Create(wxWindow *parent) { + cw = new wxComboBox(parent, -1, to_wx(value), wxDefaultPosition, wxDefaultSize, to_wx(items), wxCB_READONLY, StringBinder(&value)); + cw->SetToolTip(to_wx(hint)); return cw; } - void LuaReadBack(lua_State *L) - { - lua_pushstring(L, value.utf8_str()); + void LuaReadBack(lua_State *L) { + lua_pushstring(L, value.c_str()); } }; class Checkbox : public LuaDialogControl { - wxString label; + std::string label; bool value; wxCheckBox *cw; - public: + public: Checkbox(lua_State *L) : LuaDialogControl(L) , label(get_field(L, "label")) @@ -459,29 +384,18 @@ namespace Automation4 { } bool CanSerialiseValue() const { return true; } + std::string SerialiseValue() const { return value ? "1" : "0"; } + void UnserialiseValue(const std::string &serialised) { value = serialised != "0"; } - wxString SerialiseValue() const - { - return value ? "1" : "0"; - } - - void UnserialiseValue(const wxString &serialised) - { - // fixme? should this allow more different "false" values? - value = serialised != "0"; - } - - wxControl *Create(wxWindow *parent) - { - cw = new wxCheckBox(parent, -1, label); + wxControl *Create(wxWindow *parent) { + cw = new wxCheckBox(parent, -1, to_wx(label)); cw->SetValidator(wxGenericValidator(&value)); - cw->SetToolTip(hint); + cw->SetToolTip(to_wx(hint)); cw->SetValue(value); return cw; } - void LuaReadBack(lua_State *L) - { + void LuaReadBack(lua_State *L) { lua_pushboolean(L, value); } }; @@ -503,40 +417,38 @@ namespace Automation4 { lua_pushvalue(L, 1); lua_pushnil(L); // initial key while (lua_next(L, -2)) { - wxString controlclass; + if (!lua_istable(L, -1)) + luaL_error(L, "bad control table entry"); - if (lua_istable(L, -1)) - controlclass = get_field(L, "class"); - - controlclass.LowerCase(); + std::string controlclass = get_field(L, "class"); + boost::to_lower(controlclass); LuaDialogControl *ctl; // Check control class and create relevant control - if (controlclass == "label") { + if (controlclass == "label") ctl = new LuaControl::Label(L); - } else if (controlclass == "edit") { + else if (controlclass == "edit") ctl = new LuaControl::Edit(L); - } else if (controlclass == "intedit") { + else if (controlclass == "intedit") ctl = new LuaControl::IntEdit(L); - } else if (controlclass == "floatedit") { + else if (controlclass == "floatedit") ctl = new LuaControl::FloatEdit(L); - } else if (controlclass == "textbox") { + else if (controlclass == "textbox") ctl = new LuaControl::Textbox(L); - } else if (controlclass == "dropdown") { + else if (controlclass == "dropdown") ctl = new LuaControl::Dropdown(L); - } else if (controlclass == "checkbox") { + else if (controlclass == "checkbox") ctl = new LuaControl::Checkbox(L); - } else if (controlclass == "color") { + else if (controlclass == "color") ctl = new LuaControl::Color(L, false); - } else if (controlclass == "coloralpha") { + else if (controlclass == "coloralpha") ctl = new LuaControl::Color(L, true); - } else if (controlclass == "alpha") { + else if (controlclass == "alpha") // FIXME ctl = new LuaControl::Edit(L); - } else { + else luaL_error(L, "bad control table entry"); - } controls.push_back(ctl); lua_pop(L, 1); @@ -548,29 +460,29 @@ namespace Automation4 { } } - LuaDialog::~LuaDialog() - { + LuaDialog::~LuaDialog() { delete_clear(controls); } - wxWindow* LuaDialog::CreateWindow(wxWindow *parent) - { + wxWindow* LuaDialog::CreateWindow(wxWindow *parent) { window = new wxPanel(parent); - wxGridBagSizer *s = new wxGridBagSizer(4, 4); + wxGridBagSizer *s = new wxGridBagSizer(4, 4); for (auto c : controls) s->Add(c->Create(window), wxGBPosition(c->y, c->x), wxGBSpan(c->height, c->width), c->GetSizerFlags()); - if (use_buttons) { - wxStdDialogButtonSizer *bs = new wxStdDialogButtonSizer(); + if (!use_buttons) + window->SetSizerAndFit(s); + else { + wxStdDialogButtonSizer *bs = new wxStdDialogButtonSizer; if (buttons.size() > 0) { LOG_D("automation/lua/dialog") << "creating user buttons"; for (size_t i = 0; i < buttons.size(); ++i) { - LOG_D("automation/lua/dialog") << "button '" << from_wx(buttons[i]) << "' gets id " << 1001+(wxWindowID)i; - - bs->Add(new wxButton(window, 1001+(wxWindowID)i, buttons[i])); + LOG_D("automation/lua/dialog") << "button '" << buttons[i] << "' gets id " << 1001+(wxWindowID)i; + bs->Add(new wxButton(window, 1001+(wxWindowID)i, to_wx(buttons[i]))); } - } else { + } + else { LOG_D("automation/lua/dialog") << "creating default buttons"; bs->Add(new wxButton(window, wxID_OK)); bs->Add(new wxButton(window, wxID_CANCEL)); @@ -583,15 +495,12 @@ namespace Automation4 { ms->Add(s, 0, wxBOTTOM, 5); ms->Add(bs); window->SetSizerAndFit(ms); - } else { - window->SetSizerAndFit(s); } return window; } - int LuaDialog::LuaReadBack(lua_State *L) - { + int LuaDialog::LuaReadBack(lua_State *L) { // First read back which button was pressed, if any if (use_buttons) { LOG_D("automation/lua/dialog") << "reading back button_pushed"; @@ -600,13 +509,15 @@ namespace Automation4 { LOG_D("automation/lua/dialog") << "was zero, canceled"; // Always cancel/closed lua_pushboolean(L, 0); - } else if (buttons.size() == 0 && btn == 1) { + } + else if (buttons.size() == 0 && btn == 1) { LOG_D("automation/lua/dialog") << "default buttons, button 1 bushed, Ok button"; lua_pushboolean(L, 1); - } else { - LOG_D("automation/lua/dialog") << "user button: " << from_wx(buttons.at(btn-1)); + } + else { + LOG_D("automation/lua/dialog") << "user button: " << buttons.at(btn-1); // button_pushed is index+1 to reserve 0 for Cancel - lua_pushstring(L, buttons.at(btn-1).utf8_str()); + lua_pushstring(L, buttons.at(btn-1).c_str()); } } @@ -614,45 +525,35 @@ namespace Automation4 { lua_newtable(L); for (auto control : controls) { control->LuaReadBack(L); - lua_setfield(L, -2, control->name.utf8_str()); + lua_setfield(L, -2, control->name.c_str()); } - if (use_buttons) { - return 2; - } else { - return 1; - } + return use_buttons ? 2 : 1; } - wxString LuaDialog::Serialise() - { - wxString res; + std::string LuaDialog::Serialise() { + std::string res; - // Format into "name1:value1|name2:value2|name3:value3|" + // Format into "name1:value1|name2:value2|name3:value3" for (auto control : controls) { if (control->CanSerialiseValue()) { - wxString sn = inline_string_encode(control->name); - wxString sv = control->SerialiseValue(); - res += wxString::Format("%s:%s|", sn, sv); + if (!res.empty()) + res += "|"; + res += inline_string_encode(control->name) + ":" + control->SerialiseValue(); } } - // Remove trailing pipe - if (!res.empty()) - res.RemoveLast(); - return res; } - void LuaDialog::Unserialise(const wxString &serialised) - { - // Split by pipe - wxStringTokenizer tk(serialised, "|"); - while (tk.HasMoreTokens()) { - // Split by colon - wxString pair = tk.GetNextToken(); - wxString name = inline_string_decode(pair.BeforeFirst(':')); - wxString value = pair.AfterFirst(':'); + void LuaDialog::Unserialise(const std::string &serialised) { + boost::char_separator psep("|"), csep(":"); + for (auto const& cur : boost::tokenizer>(serialised, psep)) { + size_t pos = cur.find(':'); + if (pos == std::string::npos) continue; + + std::string name = inline_string_decode(cur.substr(0, pos)); + std::string value = cur.substr(pos + 1); // Hand value to all controls matching name for (auto control : controls) { @@ -662,17 +563,18 @@ namespace Automation4 { } } - void LuaDialog::OnButtonPush(wxCommandEvent &evt) - { + void LuaDialog::OnButtonPush(wxCommandEvent &evt) { // Let button_pushed == 0 mean "cancelled", such that pushing Cancel or closing the dialog // will both result in button_pushed == 0 if (evt.GetId() == wxID_OK) { LOG_D("automation/lua/dialog") << "was wxID_OK"; button_pushed = 1; - } else if (evt.GetId() == wxID_CANCEL) { + } + else if (evt.GetId() == wxID_CANCEL) { LOG_D("automation/lua/dialog") << "was wxID_CANCEL"; button_pushed = 0; - } else { + } + else { LOG_D("automation/lua/dialog") << "was user button"; // Therefore, when buttons are numbered from 1001 to 1000+n, make sure to set it to i+1 button_pushed = evt.GetId() - 1000; diff --git a/aegisub/src/auto4_lua_factory.h b/aegisub/src/auto4_lua_factory.h index a2411f844..3029e30b6 100644 --- a/aegisub/src/auto4_lua_factory.h +++ b/aegisub/src/auto4_lua_factory.h @@ -36,7 +36,7 @@ namespace Automation4 { class LuaScriptFactory : public ScriptFactory { - Script* Produce(const wxString &filename) const; + Script* Produce(agi::fs::path const& filename) const; public: LuaScriptFactory(); }; diff --git a/aegisub/src/auto4_lua_progresssink.cpp b/aegisub/src/auto4_lua_progresssink.cpp index c9157adbf..7a84806bb 100644 --- a/aegisub/src/auto4_lua_progresssink.cpp +++ b/aegisub/src/auto4_lua_progresssink.cpp @@ -63,7 +63,7 @@ namespace { inline wxString check_wxstring(lua_State *L, int idx) { - return wxString(luaL_checkstring(L, idx), wxConvUTF8); + return wxString::FromUTF8(luaL_checkstring(L, idx)); } } diff --git a/aegisub/src/auto4_lua_scriptreader.cpp b/aegisub/src/auto4_lua_scriptreader.cpp index 4580f7cb0..3e7f09028 100644 --- a/aegisub/src/auto4_lua_scriptreader.cpp +++ b/aegisub/src/auto4_lua_scriptreader.cpp @@ -23,16 +23,17 @@ #include "auto4_lua_scriptreader.h" +#include "charset_detect.h" + #include #include -#include "charset_detect.h" -#include "compat.h" +#include namespace Automation4 { - LuaScriptReader::LuaScriptReader(wxString const& filename) + LuaScriptReader::LuaScriptReader(agi::fs::path const& filename) : conv(new agi::charset::IconvWrapper(CharSetDetect::GetEncoding(filename).c_str(), "utf-8", false)) - , file(agi::io::Open(from_wx(filename))) + , file(agi::io::Open(filename)) { } diff --git a/aegisub/src/auto4_lua_scriptreader.h b/aegisub/src/auto4_lua_scriptreader.h index deaec1442..5e8d1291e 100644 --- a/aegisub/src/auto4_lua_scriptreader.h +++ b/aegisub/src/auto4_lua_scriptreader.h @@ -17,23 +17,24 @@ /// @ingroup scripting /// -#include +#include -#include +#include +#include +#include struct lua_State; namespace agi { namespace charset { class IconvWrapper; } } -class wxString; namespace Automation4 { class LuaScriptReader { - agi::scoped_ptr conv; - agi::scoped_ptr file; + std::unique_ptr conv; + std::unique_ptr file; char buf[512]; const char *Read(size_t *bytes_read); public: - LuaScriptReader(wxString const& filename); + LuaScriptReader(agi::fs::path const& filename); ~LuaScriptReader(); static const char* reader_func(lua_State *, void *data, size_t *size); diff --git a/aegisub/src/avisynth_wrap.cpp b/aegisub/src/avisynth_wrap.cpp index bd8331db2..52839b40a 100644 --- a/aegisub/src/avisynth_wrap.cpp +++ b/aegisub/src/avisynth_wrap.cpp @@ -40,12 +40,14 @@ #include "avisynth.h" #include "options.h" +#include + // Allocate storage for and initialise static members namespace { int avs_refcount = 0; HINSTANCE hLib = nullptr; IScriptEnvironment *env = nullptr; - wxMutex AviSynthMutex; + std::mutex AviSynthMutex; } typedef IScriptEnvironment* __stdcall FUNC(int); @@ -55,11 +57,11 @@ AviSynthWrapper::AviSynthWrapper() { hLib = LoadLibrary(L"avisynth.dll"); if (!hLib) - throw wxString("Could not load avisynth.dll"); + throw AvisynthError("Could not load avisynth.dll"); FUNC *CreateScriptEnv = (FUNC*)GetProcAddress(hLib, "CreateScriptEnvironment"); if (!CreateScriptEnv) - throw wxString("Failed to get address of CreateScriptEnv from avisynth.dll"); + throw AvisynthError("Failed to get address of CreateScriptEnv from avisynth.dll"); // Require Avisynth 2.5.6+? if (OPT_GET("Provider/Avisynth/Allow Ancient")->GetBool()) @@ -68,11 +70,11 @@ AviSynthWrapper::AviSynthWrapper() { env = CreateScriptEnv(AVISYNTH_INTERFACE_VERSION); if (!env) - throw wxString("Failed to create a new avisynth script environment. Avisynth is too old?"); + throw AvisynthError("Failed to create a new avisynth script environment. Avisynth is too old?"); // Set memory limit const int memoryMax = OPT_GET("Provider/Avisynth/Memory Max")->GetInt(); - if (memoryMax != 0) + if (memoryMax) env->SetMemoryMax(memoryMax); } } @@ -84,7 +86,7 @@ AviSynthWrapper::~AviSynthWrapper() { } } -wxMutex& AviSynthWrapper::GetMutex() const { +std::mutex& AviSynthWrapper::GetMutex() const { return AviSynthMutex; } diff --git a/aegisub/src/avisynth_wrap.h b/aegisub/src/avisynth_wrap.h index d4e5b2e2a..ece6fac63 100644 --- a/aegisub/src/avisynth_wrap.h +++ b/aegisub/src/avisynth_wrap.h @@ -34,12 +34,15 @@ #ifdef WITH_AVISYNTH +#include + class IScriptEnvironment; +namespace std { class mutex; } class AviSynthWrapper { AviSynthWrapper(AviSynthWrapper const&); public: - wxMutex& GetMutex() const; + std::mutex& GetMutex() const; IScriptEnvironment *GetEnv() const; AviSynthWrapper(); diff --git a/aegisub/src/base_grid.cpp b/aegisub/src/base_grid.cpp index d60d827e5..ad159ea99 100644 --- a/aegisub/src/base_grid.cpp +++ b/aegisub/src/base_grid.cpp @@ -191,8 +191,8 @@ void BaseGrid::OnSubtitlesOpen() { } void BaseGrid::OnSubtitlesSave() { - context->ass->SetScriptInfo("Scroll Position", wxString::Format("%d", yPos)); - context->ass->SetScriptInfo("Active Line", wxString::Format("%d", GetDialogueIndex(active_line))); + context->ass->SetScriptInfo("Scroll Position", std::to_string(yPos)); + context->ass->SetScriptInfo("Active Line", std::to_string(GetDialogueIndex(active_line))); } void BaseGrid::OnShowColMenu(wxCommandEvent &event) { @@ -592,15 +592,15 @@ void BaseGrid::GetRowStrings(int row, AssDialogue *line, bool *paint_columns, wx if (paint_columns[3]) strings[3] = wxString::Format("%d", context->videoController->FrameAtTime(line->End, agi::vfr::END)); } else { - if (paint_columns[2]) strings[2] = line->Start.GetAssFormated(); - if (paint_columns[3]) strings[3] = line->End.GetAssFormated(); + if (paint_columns[2]) strings[2] = to_wx(line->Start.GetAssFormated()); + if (paint_columns[3]) strings[3] = to_wx(line->End.GetAssFormated()); } - if (paint_columns[4]) strings[4] = line->Style; - if (paint_columns[5]) strings[5] = line->Actor; - if (paint_columns[6]) strings[6] = line->Effect; - if (paint_columns[7]) strings[7] = line->GetMarginString(0); - if (paint_columns[8]) strings[8] = line->GetMarginString(1); - if (paint_columns[9]) strings[9] = line->GetMarginString(2); + if (paint_columns[4]) strings[4] = to_wx(line->Style); + if (paint_columns[5]) strings[5] = to_wx(line->Actor); + if (paint_columns[6]) strings[6] = to_wx(line->Effect); + if (paint_columns[7]) strings[7] = to_wx(line->GetMarginString(0)); + if (paint_columns[8]) strings[8] = to_wx(line->GetMarginString(1)); + if (paint_columns[9]) strings[9] = to_wx(line->GetMarginString(2)); if (paint_columns[10]) { strings[10].clear(); @@ -609,18 +609,19 @@ void BaseGrid::GetRowStrings(int row, AssDialogue *line, bool *paint_columns, wx if (replace) { strings[10].reserve(line->Text.get().size()); size_t start = 0, pos; - while ((pos = line->Text.get().find('{', start)) != wxString::npos) { - strings[10] += line->Text.get().Mid(start, pos - start); + while ((pos = line->Text.get().find('{', start)) != std::string::npos) { + strings[10] += to_wx(line->Text.get().substr(start, pos - start)); strings[10] += rep_char; start = line->Text.get().find('}', pos); - if (start != wxString::npos) ++start; + if (start != std::string::npos) ++start; } - strings[10] += line->Text.get().Mid(start); + if (start != std::string::npos) + strings[10] += to_wx(line->Text.get().substr(start)); } // Show overrides else - strings[10] = line->Text; + strings[10] = to_wx(line->Text); // Cap length and set text if (strings[10].size() > 512) @@ -833,7 +834,7 @@ void BaseGrid::SetColumnWidths() { int startLen = 0; int endLen = 0; if (!byFrame) - startLen = endLen = dc.GetTextExtent(AssTime().GetAssFormated()).GetWidth(); + startLen = endLen = dc.GetTextExtent(to_wx(AssTime().GetAssFormated())).GetWidth(); // O(n) widths bool showMargin[3] = { false, false, false }; @@ -847,9 +848,9 @@ void BaseGrid::SetColumnWidths() { AssDialogue *curDiag = GetDialogue(i); maxLayer = std::max(maxLayer, curDiag->Layer); - actorLen = std::max(actorLen, dc.GetTextExtent(curDiag->Actor).GetWidth()); - styleLen = std::max(styleLen, dc.GetTextExtent(curDiag->Style).GetWidth()); - effectLen = std::max(effectLen, dc.GetTextExtent(curDiag->Effect).GetWidth()); + actorLen = std::max(actorLen, dc.GetTextExtent(to_wx(curDiag->Actor)).GetWidth()); + styleLen = std::max(styleLen, dc.GetTextExtent(to_wx(curDiag->Style)).GetWidth()); + effectLen = std::max(effectLen, dc.GetTextExtent(to_wx(curDiag->Effect)).GetWidth()); // Margins for (int j = 0; j < 3; j++) { diff --git a/aegisub/src/charset_conv.cpp b/aegisub/src/charset_conv.cpp deleted file mode 100644 index 01a796898..000000000 --- a/aegisub/src/charset_conv.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2009, Thomas Goyne -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Aegisub Project http://www.aegisub.org/ - -/// @file charset_conv.cpp -/// @brief Iconv-based implementation of character set conversions -/// @ingroup utility -/// - -#include "config.h" - -#include "charset_conv.h" - -#include -#include - -#include - -class AegisubCSConvImpl : public AegisubCSConv { -public: - AegisubCSConvImpl() { } -}; - -AegisubCSConv::AegisubCSConv() -: conv("wchar_t", "") -{ -} - -size_t AegisubCSConv::ToWChar(wchar_t *dst, size_t dstSize, const char *src, size_t srcLen) const { - throw agi::charset::UnsupportedConversion("Cannot convert to local with csConvLocal"); -} - -/// @brief Convert a string from wide characters to multibyte -/// @param dst Destination buffer -/// @param dstSize Length of destination buffer in bytes -/// @param src Source wide character string -/// @param srcLen Length in wchar_ts of source, or -1 to autodetect -/// @return The number of bytes needed to store the string in the target charset -size_t AegisubCSConv::FromWChar(char *dst, size_t dstSize, const wchar_t *src, size_t srcLen) const { - try { - if (srcLen != (size_t)-1) { - if (src[srcLen - 1] == 0) srcLen -= 1; - srcLen *= sizeof(wchar_t); - } - if (dstSize == 0) { - return conv.RequiredBufferSize(reinterpret_cast(src), srcLen); - } - return conv.Convert(reinterpret_cast(src), srcLen, dst, dstSize); - } - catch (agi::charset::ConvError const&) { - return (size_t)-1; - } -} -static AegisubCSConvImpl localConv; -AegisubCSConv& csConvLocal = localConv; diff --git a/aegisub/src/charset_conv.h b/aegisub/src/charset_conv.h deleted file mode 100644 index da8224a9f..000000000 --- a/aegisub/src/charset_conv.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2012, Thomas Goyne -// -// 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/ - -/// @file charset_conv.h -/// @see charset_conv.cpp -/// @ingroup utility -/// - -#include -#include -#include - -#include - -/// @class AegisubCSConv -/// @brief wxMBConv implementation for converting to and from unicode -class AegisubCSConv : public wxMBConv { -public: - - // wxMBConv implementation; see strconv.h for usage details - size_t ToWChar(wchar_t *dst, size_t dstLen, const char *src, size_t srcLen = wxNO_LEN) const; - size_t FromWChar(char *dst, size_t dstLen, const wchar_t *src, size_t srcLen = wxNO_LEN) const; - wxMBConv *Clone() const { return nullptr; }; - -protected: - AegisubCSConv(); -private: - AegisubCSConv(const AegisubCSConv&); - AegisubCSConv& operator=(const AegisubCSConv&); - wxString localCharset; - - mutable wxMutex iconvMutex; - - // ToWChar and FromWChar are const in wxMBConv, but iconv can't be used - // immutably - mutable agi::charset::IconvWrapper conv; -}; - -extern AegisubCSConv& csConvLocal; diff --git a/aegisub/src/charset_detect.cpp b/aegisub/src/charset_detect.cpp index 6bdf4371b..a61c65345 100644 --- a/aegisub/src/charset_detect.cpp +++ b/aegisub/src/charset_detect.cpp @@ -34,26 +34,26 @@ #include "config.h" -#include -#include +#include "charset_detect.h" + +#include "compat.h" + +#include +#include + +#include #include #include #include -#include -#include - -#include "charset_detect.h" -#include "compat.h" - namespace CharSetDetect { -wxString GetEncoding(wxString const& filename) { +std::string GetEncoding(agi::fs::path const& filename) { agi::charset::CharsetListDetected list; try { - list = agi::charset::DetectAll(from_wx(filename)); + list = agi::charset::DetectAll(filename); } catch (const agi::charset::UnknownCharset&) { /// @todo If the charset is unknown we need to display a complete list of character sets. } @@ -61,7 +61,7 @@ wxString GetEncoding(wxString const& filename) { if (list.size() == 1) { auto charset = list.begin(); LOG_I("charset/file") << filename << " (" << charset->second << ")"; - return to_wx(charset->second); + return charset->second; } wxArrayString choices; @@ -74,9 +74,12 @@ wxString GetEncoding(wxString const& filename) { LOG_I("charset/file") << filename << " (" << log_choice << ")"; - int choice = wxGetSingleChoiceIndex(_("Aegisub could not narrow down the character set to a single one.\nPlease pick one below:"),_("Choose character set"),choices); - if (choice == -1) throw "Canceled"; - return choices[choice]; + int choice = wxGetSingleChoiceIndex( + _("Aegisub could not narrow down the character set to a single one.\nPlease pick one below:"), + _("Choose character set"), + choices); + if (choice == -1) throw agi::UserCancelException("Cancelled encoding selection"); + return list[choice].second; } } diff --git a/aegisub/src/charset_detect.h b/aegisub/src/charset_detect.h index 0307245ae..ec528a598 100644 --- a/aegisub/src/charset_detect.h +++ b/aegisub/src/charset_detect.h @@ -32,9 +32,12 @@ /// @ingroup utility /// +#include +#include + namespace CharSetDetect { /// @brief Get character set name. /// @param filename File to check /// @return Character set name - wxString GetEncoding(wxString const& filename); + std::string GetEncoding(agi::fs::path const& filename); } diff --git a/aegisub/src/colorspace.cpp b/aegisub/src/colorspace.cpp index bbf98ea2c..b63506d38 100644 --- a/aegisub/src/colorspace.cpp +++ b/aegisub/src/colorspace.cpp @@ -37,19 +37,11 @@ #include "colorspace.h" #include "utils.h" - -// Matrix from http://forum.doom9.org/showthread.php?p=684080#post684080 -void yuv_to_rgb(int Y, int U, int V, unsigned char *R, unsigned char *G, unsigned char *B) +static inline unsigned int clip_colorval(int val) { - U = U - 128; - V = V - 128; - *R = clip_colorval((Y*65536 + int(1.140*65536) * V) / 65536); - *G = clip_colorval((Y*65536 - int(0.395*65536) * U - int(0.581*65536) * V) / 65536); - *B = clip_colorval((Y*65536 + int(2.032*65536) * U ) / 65536); + return mid(0, val, 255); } - - // Algorithm from http://130.113.54.154/~monger/hsl-rgb.html void hsl_to_rgb(int H, int S, int L, unsigned char *R, unsigned char *G, unsigned char *B) { @@ -253,14 +245,6 @@ void hsv_to_rgb(int H, int S, int V, unsigned char *R, unsigned char *G, unsigne *B = clip_colorval(b/256); } -// Matrix from http://forum.doom9.org/showthread.php?p=684080#post684080 -void rgb_to_yuv(int R, int G, int B, unsigned char *Y, unsigned char *U, unsigned char *V) -{ - *Y = clip_colorval(( int(0.299*65536) * R + int(0.587*65536) * G + int(0.114*65536) * B) / 65536); - *U = clip_colorval((-int(0.147*65536) * R - int(0.289*65536) * G + int(0.436*65536) * B) / 65536 + 128); - *V = clip_colorval(( int(0.615*65536) * R - int(0.515*65536) * G - int(0.100*65536) * B) / 65536 + 128); -} - /// Algorithm from http://130.113.54.154/~monger/hsl-rgb.html void rgb_to_hsl(int R, int G, int B, unsigned char *H, unsigned char *S, unsigned char *L) { diff --git a/aegisub/src/colorspace.h b/aegisub/src/colorspace.h index 18296490e..ce1640bc2 100644 --- a/aegisub/src/colorspace.h +++ b/aegisub/src/colorspace.h @@ -32,27 +32,12 @@ /// @ingroup utility /// -#include -#include - -inline unsigned int clip_colorval(int val) -{ - return std::max(0, std::min(val, 255)); -} - - -/// Convert an YUV color to RGB; all values are expected to be in range 0..255 -void yuv_to_rgb(int Y, int U, int V, unsigned char *R, unsigned char *G, unsigned char *B); - /// Convert a HSL color to RGB; all values are expected to be in range 0..255 void hsl_to_rgb(int H, int S, int L, unsigned char *R, unsigned char *G, unsigned char *B); /// Convert a HSV color to RGB; all values are expected to be in range 0..255 void hsv_to_rgb(int H, int S, int V, unsigned char *R, unsigned char *G, unsigned char *B); -/// Convert an RGB color to YUV; all values are expected to be in range 0..255 -void rgb_to_yuv(int R, int G, int B, unsigned char *Y, unsigned char *U, unsigned char *V); - /// Convert an RGB color to HSL; all values are expected to be in range 0..255 void rgb_to_hsl(int R, int G, int B, unsigned char *H, unsigned char *S, unsigned char *L); diff --git a/aegisub/src/command/audio.cpp b/aegisub/src/command/audio.cpp index 00c9402f3..3fb14f58f 100644 --- a/aegisub/src/command/audio.cpp +++ b/aegisub/src/command/audio.cpp @@ -36,10 +36,6 @@ #include "../config.h" -#include -#include -#include - #include "command.h" #include "../ass_dialogue.h" @@ -53,6 +49,10 @@ #include "../selection_controller.h" #include "../video_context.h" +#include +#include +#include + namespace { using cmd::Command; @@ -92,10 +92,10 @@ struct audio_open : public Command { wxString str = _("Audio Formats") + " (*.aac,*.ac3,*.ape,*.dts,*.flac,*.m4a,*.mka,*.mp3,*.mp4,*.ogg,*.w64,*.wav,*.wma)|*.aac;*.ac3;*.ape;*.dts;*.flac;*.m4a;*.mka;*.mp3;*.mp4;*.ogg;*.w64;*.wav;*.wma|" + _("Video Formats") + " (*.asf,*.avi,*.avs,*.d2v,*.m2ts,*.m4v,*.mkv,*.mov,*.mp4,*.mpeg,*.mpg,*.ogm,*.webm,*.wmv,*.ts)|*.asf;*.avi;*.avs;*.d2v;*.m2ts;*.m4v;*.mkv;*.mov;*.mp4;*.mpeg;*.mpg;*.ogm;*.webm;*.wmv;*.ts|" + _("All Files") + " (*.*)|*.*"; - wxString filename = wxFileSelector(_("Open Audio File"),path,"","",str,wxFD_OPEN | wxFD_FILE_MUST_EXIST); + agi::fs::path filename = wxFileSelector(_("Open Audio File"),path,"","",str,wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (!filename.empty()) { c->audioController->OpenAudio(filename); - OPT_SET("Path/Last/Audio")->SetString(from_wx(wxFileName(filename).GetPath())); + OPT_SET("Path/Last/Audio")->SetString(filename.parent_path().string()); } } catch (agi::UserCancelException const&) { } @@ -224,7 +224,7 @@ struct audio_save_clip : public Command { } c->audioController->SaveClip( - wxFileSelector(_("Save audio clip"), "", "", "wav", "", wxFD_SAVE|wxFD_OVERWRITE_PROMPT, c->parent), + from_wx(wxFileSelector(_("Save audio clip"), "", "", "wav", "", wxFD_SAVE|wxFD_OVERWRITE_PROMPT, c->parent)), TimeRange(start, end)); } }; diff --git a/aegisub/src/command/edit.cpp b/aegisub/src/command/edit.cpp index ca6423459..c5872dec1 100644 --- a/aegisub/src/command/edit.cpp +++ b/aegisub/src/command/edit.cpp @@ -36,12 +36,6 @@ #include "../config.h" -#include - -#include -#include -#include - #include "command.h" #include "../ass_dialogue.h" @@ -62,15 +56,21 @@ #include "../utils.h" #include "../video_context.h" -#include +#include + +#include #include +#include +#include #include #include #include #include #include +#include -#include +#include +#include namespace { using namespace boost::adaptors; @@ -94,16 +94,15 @@ struct validate_sel_multiple : public Command { template void paste_lines(agi::Context *c, bool paste_over, Paster&& paste_line) { - wxString data = GetClipboard(); - if (!data) return; + std::string data = GetClipboard(); + if (data.empty()) return; AssDialogue *first = nullptr; SubtitleSelection newsel; - wxStringTokenizer token(data, "\r\n", wxTOKEN_STRTOK); - while (token.HasMoreTokens()) { - // Convert data into an AssDialogue - wxString curdata = token.GetNextToken().Trim(true).Trim(false); + boost::char_separator sep("\r\n"); + for (auto curdata : boost::tokenizer>(data, sep)) { + boost::trim(curdata); AssDialogue *curdiag; try { // Try to interpret the line as an ASS line @@ -166,7 +165,7 @@ T get_value(boost::ptr_vector const& blocks, int blockn, T ini } /// Get the block index in the text of the position -int block_at_pos(wxString const& text, int pos) { +int block_at_pos(std::string const& text, int pos) { int n = 0; int max = text.size() - 1; for (int i = 0; i <= pos && i <= max; ++i) { @@ -214,7 +213,7 @@ void set_tag(AssDialogue *line, boost::ptr_vector &blocks, std std::string insert(tag + value); int shift = insert.size(); if (plain || blockn < 0) { - line->Text = line->Text.get().Left(start) + "{" + to_wx(insert) + "}" + line->Text.get().Mid(start); + line->Text = line->Text.get().substr(0, start) + "{" + insert + "}" + line->Text.get().substr(start); shift += 2; blocks = line->ParseTags(); } @@ -253,7 +252,7 @@ void set_tag(AssDialogue *line, boost::ptr_vector &blocks, std void commit_text(agi::Context const * const c, wxString const& desc, int sel_start = -1, int sel_end = -1, int *commit_id = 0) { SubtitleSelection const& sel = c->selectionController->GetSelectedSet(); - wxString text = c->selectionController->GetActiveLine()->Text; + std::string text = c->selectionController->GetActiveLine()->Text; for_each(sel.begin(), sel.end(), [&](AssDialogue *d) { d->Text = text; }); int new_commit_id = c->ass->Commit(desc, AssFile::COMMIT_DIAG_TEXT, commit_id ? *commit_id : -1, sel.size() == 1 ? *sel.begin() : 0); @@ -265,7 +264,7 @@ void commit_text(agi::Context const * const c, wxString const& desc, int sel_sta void toggle_override_tag(const agi::Context *c, bool (AssStyle::*field), const char *tag, wxString const& undo_msg) { AssDialogue *const line = c->selectionController->GetActiveLine(); - AssStyle const* const style = c->ass->GetStyle(from_wx(line->Style)); + AssStyle const* const style = c->ass->GetStyle(line->Style); bool state = style ? style->*field : AssStyle().*field; boost::ptr_vector blocks(line->ParseTags()); @@ -284,7 +283,7 @@ void toggle_override_tag(const agi::Context *c, bool (AssStyle::*field), const c void show_color_picker(const agi::Context *c, agi::Color (AssStyle::*field), const char *tag, const char *alt, const char *alpha) { AssDialogue *const line = c->selectionController->GetActiveLine(); - AssStyle const* const style = c->ass->GetStyle(from_wx(line->Style)); + AssStyle const* const style = c->ass->GetStyle(line->Style); agi::Color color = (style ? style->*field : AssStyle().*field); boost::ptr_vector blocks(line->ParseTags()); @@ -412,7 +411,7 @@ struct edit_font : public Command { boost::ptr_vector blocks(line->ParseTags()); const int blockn = block_at_pos(line->Text, c->textSelectionController->GetInsertionPoint()); - const AssStyle *style = c->ass->GetStyle(from_wx(line->Style)); + const AssStyle *style = c->ass->GetStyle(line->Style); const AssStyle default_style; if (!style) style = &default_style; @@ -426,7 +425,7 @@ struct edit_font : public Command { get_value(blocks, blockn, style->italic, "\\i") ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL, get_value(blocks, blockn, style->bold, "\\b") ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL, get_value(blocks, blockn, style->underline, "\\u"), - to_wx(get_value(blocks, blockn, from_wx(style->font), "\\fn"))); + to_wx(get_value(blocks, blockn, style->font, "\\fn"))); const wxFont font = wxGetFontFromUser(c->parent, startfont); if (!font.Ok() || font == startfont) return; @@ -459,14 +458,14 @@ struct edit_find_replace : public Command { } }; -static wxString get_entry_data(AssDialogue *d) { return d->GetEntryData(); } +static std::string get_entry_data(AssDialogue *d) { return d->GetEntryData(); } static void copy_lines(agi::Context *c) { SubtitleSelection sel = c->selectionController->GetSelectedSet(); SetClipboard(join(c->ass->Line | agi::of_type() | filtered([&](AssDialogue *d) { return sel.count(d); }) | transformed(get_entry_data), - wxS("\r\n"))); + "\r\n")); } static void delete_lines(agi::Context *c, wxString const& commit_message) { @@ -668,11 +667,11 @@ static void combine_lines(agi::Context *c, void (*combiner)(AssDialogue *, AssDi } static void combine_karaoke(AssDialogue *first, AssDialogue *second) { - first->Text = wxString::Format("%s{\\k%d}%s", first->Text.get(), (second->Start - first->End) / 10, second->Text.get()); + first->Text = first->Text.get() + "{\\k" + std::to_string((second->Start - first->End) / 10) + "}" + second->Text.get(); } static void combine_concat(AssDialogue *first, AssDialogue *second) { - first->Text = first->Text + " " + second->Text; + first->Text = first->Text.get() + " " + second->Text.get(); } static void combine_drop(AssDialogue *, AssDialogue *) { } @@ -836,9 +835,9 @@ void split_lines(agi::Context *c, bool estimate) { AssDialogue *n2 = new AssDialogue(*n1); c->ass->Line.insert(++c->ass->Line.iterator_to(*n1), *n2); - wxString orig = n1->Text; - n1->Text = orig.Left(pos).Trim(true); // Trim off trailing whitespace - n2->Text = orig.Mid(pos).Trim(false); // Trim off leading whitespace + std::string orig = n1->Text; + n1->Text = boost::trim_right_copy(orig.substr(0, pos)); + n2->Text = boost::trim_left_copy(orig.substr(pos)); if (estimate && orig.size()) { double splitPos = double(pos) / orig.size(); @@ -948,7 +947,7 @@ struct edit_clear : public Command { } }; -wxString get_text(AssDialogueBlock &d) { return d.GetText(); } +std::string get_text(AssDialogueBlock &d) { return d.GetText(); } struct edit_clear_text : public Command { CMD_NAME("edit/clear/text") STR_DISP("Clear Text") @@ -961,7 +960,7 @@ struct edit_clear_text : public Command { line->Text = join(blocks | filtered([](AssDialogueBlock const& b) { return b.GetType() != BLOCK_PLAIN; }) | transformed(get_text), - wxS("")); + ""); c->ass->Commit(_("clear line"), AssFile::COMMIT_DIAG_TEXT, -1, line); } }; @@ -977,7 +976,7 @@ struct edit_insert_original : public Command { int sel_start = c->textSelectionController->GetSelectionStart(); int sel_end = c->textSelectionController->GetSelectionEnd(); - line->Text = line->Text.get().Left(sel_start) + c->initialLineState->GetInitialText() + line->Text.get().Mid(sel_end); + line->Text = line->Text.get().substr(0, sel_start) + c->initialLineState->GetInitialText() + line->Text.get().substr(sel_end); c->ass->Commit(_("insert original"), AssFile::COMMIT_DIAG_TEXT, -1, line); } }; diff --git a/aegisub/src/command/keyframe.cpp b/aegisub/src/command/keyframe.cpp index 43f700ec3..1075e8c49 100644 --- a/aegisub/src/command/keyframe.cpp +++ b/aegisub/src/command/keyframe.cpp @@ -36,22 +36,20 @@ #include "../config.h" -#include -#include - #include "command.h" -#include "../compat.h" #include "../include/aegisub/context.h" #include "../options.h" #include "../video_context.h" +#include +#include + namespace { using cmd::Command; /// @defgroup cmd-keyframed Keyframe commands. /// @{ - /// Closes the currently open keyframes list. struct keyframe_close : public Command { CMD_NAME("keyframe/close") @@ -78,22 +76,20 @@ struct keyframe_open : public Command { STR_HELP("Opens a keyframe list file") void operator()(agi::Context *c) { - wxString path = to_wx(OPT_GET("Path/Last/Keyframes")->GetString()); - wxString filename = wxFileSelector( + agi::fs::path filename = wxFileSelector( _("Open keyframes file"), - path, + to_wx(OPT_GET("Path/Last/Keyframes")->GetString()), "" ,".txt", _("All Supported Formats") + " (*.txt, *.pass, *.stats, *.log)|*.txt;*.pass;*.stats;*.log|" + _("All Files") + " (*.*)|*.*", wxFD_FILE_MUST_EXIST | wxFD_OPEN); if (filename.empty()) return; - OPT_SET("Path/Last/Keyframes")->SetString(from_wx(wxFileName(filename).GetPath())); + OPT_SET("Path/Last/Keyframes")->SetString(filename.parent_path().string()); c->videoController->LoadKeyframes(filename); } }; - /// Saves the current keyframe list. struct keyframe_save : public Command { CMD_NAME("keyframe/save") @@ -107,10 +103,10 @@ struct keyframe_save : public Command { } void operator()(agi::Context *c) { - wxString path = to_wx(OPT_GET("Path/Last/Keyframes")->GetString()); - wxString filename = wxFileSelector(_("Save keyframes file"),path,"","*.key.txt","Text files (*.txt)|*.txt",wxFD_OVERWRITE_PROMPT | wxFD_SAVE); + std::string path = OPT_GET("Path/Last/Keyframes")->GetString(); + agi::fs::path filename = wxFileSelector(_("Save keyframes file"),to_wx(path),"","*.key.txt","Text files (*.txt)|*.txt",wxFD_OVERWRITE_PROMPT | wxFD_SAVE); if (filename.empty()) return; - OPT_SET("Path/Last/Keyframes")->SetString(from_wx(wxFileName(filename).GetPath())); + OPT_SET("Path/Last/Keyframes")->SetString(filename.parent_path().string()); c->videoController->SaveKeyframes(filename); } }; diff --git a/aegisub/src/command/recent.cpp b/aegisub/src/command/recent.cpp index 6c6d4b33e..b448e4ea3 100644 --- a/aegisub/src/command/recent.cpp +++ b/aegisub/src/command/recent.cpp @@ -34,11 +34,6 @@ #include "../config.h" -#include - -#include -#include - #include "command.h" #include "../audio_controller.h" @@ -49,6 +44,9 @@ #include "../options.h" #include "../video_context.h" +#include +#include + namespace { using cmd::Command; /// @defgroup cmd-recent MRU (Most Recently Used) commands. @@ -68,7 +66,7 @@ struct recent_audio_entry : public Command { void operator()(agi::Context *c, int id) { try { - c->audioController->OpenAudio(to_wx(config::mru->GetEntry("Audio", id))); + c->audioController->OpenAudio(config::mru->GetEntry("Audio", id)); } catch (agi::UserCancelException const&) { } catch (agi::Exception const& e) { @@ -84,7 +82,7 @@ struct recent_keyframes_entry : public Command { STR_HELP("Open recent keyframes") void operator()(agi::Context *c, int id) { - c->videoController->LoadKeyframes(to_wx(config::mru->GetEntry("Keyframes", id))); + c->videoController->LoadKeyframes(config::mru->GetEntry("Keyframes", id)); } }; @@ -95,7 +93,7 @@ struct recent_subtitle_entry : public Command { STR_HELP("Open recent subtitles") void operator()(agi::Context *c, int id) { - wxGetApp().frame->LoadSubtitles(to_wx(config::mru->GetEntry("Subtitle", id))); + wxGetApp().frame->LoadSubtitles(config::mru->GetEntry("Subtitle", id)); } }; @@ -106,7 +104,7 @@ struct recent_timecodes_entry : public Command { STR_HELP("Open recent timecodes") void operator()(agi::Context *c, int id) { - c->videoController->LoadTimecodes(to_wx(config::mru->GetEntry("Timecodes", id))); + c->videoController->LoadTimecodes(config::mru->GetEntry("Timecodes", id)); } }; @@ -117,7 +115,7 @@ struct recent_video_entry : public Command { STR_HELP("Open recent videos") void operator()(agi::Context *c, int id) { - c->videoController->SetVideo(to_wx(config::mru->GetEntry("Video", id))); + c->videoController->SetVideo(config::mru->GetEntry("Video", id)); } }; @@ -132,12 +130,7 @@ public: void operator()(agi::Context *c) { T::operator()(c, id); } - mru_wrapper(int id) : id(id) { - std::stringstream ss; - ss << T::name(); - ss << id; - full_name = ss.str(); - } + mru_wrapper(int id) : id(id) , full_name(T::name() + std::to_string(id)) { } }; } /// @} diff --git a/aegisub/src/command/subtitle.cpp b/aegisub/src/command/subtitle.cpp index 6e0643454..70011c37e 100644 --- a/aegisub/src/command/subtitle.cpp +++ b/aegisub/src/command/subtitle.cpp @@ -36,13 +36,6 @@ #include "../config.h" -#include -#include -#include -#include - -#include - #include "command.h" #include "../ass_dialogue.h" @@ -64,6 +57,12 @@ #include "../utils.h" #include "../video_context.h" +#include +#include +#include + +#include + namespace { using cmd::Command; /// @defgroup cmd-subtitle Subtitle commands. @@ -110,7 +109,6 @@ struct subtitle_find : public Command { } }; - /// Find next match of last word. struct subtitle_find_next : public Command { CMD_NAME("subtitle/find/next") @@ -263,11 +261,15 @@ struct subtitle_open : public Command { STR_HELP("Opens a subtitles file") void operator()(agi::Context *c) { - wxString path = to_wx(OPT_GET("Path/Last/Subtitles")->GetString()); - wxString filename = wxFileSelector(_("Open subtitles file"),path,"","",SubtitleFormat::GetWildcards(0),wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (!filename.empty()) { + std::string filename = from_wx(wxFileSelector( + _("Open subtitles file"), + to_wx(OPT_GET("Path/Last/Subtitles")->GetString()), + "","", + to_wx(SubtitleFormat::GetWildcards(0)), + wxFD_OPEN | wxFD_FILE_MUST_EXIST)); + + if (!filename.empty()) wxGetApp().frame->LoadSubtitles(filename); - } } }; @@ -284,7 +286,6 @@ struct subtitle_open_autosave : public Command { } }; - /// Opens a subtitles file with a specific charset. struct subtitle_open_charset : public Command { CMD_NAME("subtitle/open/charset") @@ -293,21 +294,17 @@ struct subtitle_open_charset : public Command { STR_HELP("Opens a subtitles file with a specific charset") void operator()(agi::Context *c) { - // Initialize charsets wxString path = to_wx(OPT_GET("Path/Last/Subtitles")->GetString()); + wxString filename = wxFileSelector(_("Open subtitles file"),path,"","",to_wx(SubtitleFormat::GetWildcards(0)),wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (filename.empty()) return; - // Get options and load - wxString filename = wxFileSelector(_("Open subtitles file"),path,"","",SubtitleFormat::GetWildcards(0),wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (!filename.empty()) { - wxString charset = wxGetSingleChoice(_("Choose charset code:"), _("Charset"), agi::charset::GetEncodingsList(), c->parent, -1, -1, true, 250, 200); - if (!charset.empty()) { - wxGetApp().frame->LoadSubtitles(filename,charset); - } - } + wxString charset = wxGetSingleChoice(_("Choose charset code:"), _("Charset"), agi::charset::GetEncodingsList(), c->parent, -1, -1, true, 250, 200); + if (charset.empty()) return; + + wxGetApp().frame->LoadSubtitles(from_wx(filename), from_wx(charset)); } }; - /// Opens the subtitles from the current video file. struct subtitle_open_video : public Command { CMD_NAME("subtitle/open/video") @@ -339,12 +336,14 @@ struct subtitle_properties : public Command { } }; -static void save_subtitles(agi::Context *c, wxString filename) { +static void save_subtitles(agi::Context *c, agi::fs::path filename) { if (filename.empty()) { c->videoController->Stop(); wxString path = to_wx(OPT_GET("Path/Last/Subtitles")->GetString()); - wxFileName origPath(c->ass->filename); - filename = wxFileSelector(_("Save subtitles file"), path, origPath.GetName() + ".ass", "ass", "Advanced Substation Alpha (*.ass)|*.ass", wxFD_SAVE | wxFD_OVERWRITE_PROMPT, c->parent); + filename = wxFileSelector(_("Save subtitles file"), path, + c->ass->filename.stem().wstring() + L".ass", "ass", + "Advanced Substation Alpha (*.ass)|*.ass", + wxFD_SAVE | wxFD_OVERWRITE_PROMPT, c->parent); if (filename.empty()) return; } @@ -403,7 +402,7 @@ struct subtitle_select_all : public Command { SubtitleSelection sel; transform(c->ass->Line.begin(), c->ass->Line.end(), inserter(sel, sel.begin()), cast()); - sel.erase(0); + sel.erase(nullptr); c->selectionController->SetSelectedSet(sel); } }; diff --git a/aegisub/src/command/time.cpp b/aegisub/src/command/time.cpp index 2b3fc8c59..a0223685b 100644 --- a/aegisub/src/command/time.cpp +++ b/aegisub/src/command/time.cpp @@ -36,8 +36,6 @@ #include "../config.h" -#include - #include "command.h" #include "../ass_dialogue.h" @@ -54,6 +52,8 @@ #include +#include + namespace { using cmd::Command; @@ -73,8 +73,7 @@ namespace { size_t found = 0; for (auto diag : c->ass->Line | agi::of_type()) { if (sel.count(diag)) { - ++found; - if (found == sel.size()) + if (++found == sel.size()) return true; } else if (found) @@ -197,15 +196,13 @@ struct time_snap_scene : public validate_video_loaded { VideoContext *con = c->videoController; if (!con->IsLoaded() || !con->KeyFramesLoaded()) return; - // Get frames int curFrame = con->GetFrameN(); int prev = 0; int next = 0; const std::vector &keyframes = con->GetKeyFrames(); - if (curFrame < keyframes.front()) { + if (curFrame < keyframes.front()) next = keyframes.front(); - } else if (curFrame >= keyframes.back()) { prev = keyframes.back(); next = con->GetLength(); @@ -222,17 +219,14 @@ struct time_snap_scene : public validate_video_loaded { } } - // Get times int start_ms = con->TimeAtFrame(prev,agi::vfr::START); int end_ms = con->TimeAtFrame(next-1,agi::vfr::END); - // Update rows for (auto line : c->selectionController->GetSelectedSet()) { line->Start = start_ms; line->End = end_ms; } - // Commit c->ass->Commit(_("snap to scene"), AssFile::COMMIT_DIAG_TIME); } }; diff --git a/aegisub/src/command/timecode.cpp b/aegisub/src/command/timecode.cpp index 60f728755..bc7feaa41 100644 --- a/aegisub/src/command/timecode.cpp +++ b/aegisub/src/command/timecode.cpp @@ -36,16 +36,15 @@ #include "../config.h" -#include -#include - #include "command.h" -#include "../compat.h" #include "../include/aegisub/context.h" #include "../options.h" #include "../video_context.h" +#include +#include + namespace { using cmd::Command; /// @defgroup cmd-timecode Timecode commands. @@ -79,15 +78,14 @@ struct timecode_open : public Command { void operator()(agi::Context *c) { wxString path = to_wx(OPT_GET("Path/Last/Timecodes")->GetString()); wxString str = _("All Supported Formats") + " (*.txt)|*.txt|" + _("All Files") + " (*.*)|*.*"; - wxString filename = wxFileSelector(_("Open Timecodes File"),path,"","",str,wxFD_OPEN | wxFD_FILE_MUST_EXIST); + agi::fs::path filename = wxFileSelector(_("Open Timecodes File"),path,"","",str,wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (!filename.empty()) { c->videoController->LoadTimecodes(filename); - OPT_SET("Path/Last/Timecodes")->SetString(from_wx(wxFileName(filename).GetPath())); + OPT_SET("Path/Last/Timecodes")->SetString(filename.parent_path().string()); } } }; - /// Saves a VFR timecodes v2 file. struct timecode_save : public Command { CMD_NAME("timecode/save") @@ -103,10 +101,10 @@ struct timecode_save : public Command { void operator()(agi::Context *c) { wxString path = to_wx(OPT_GET("Path/Last/Timecodes")->GetString()); wxString str = _("All Supported Formats") + " (*.txt)|*.txt|" + _("All Files") + " (*.*)|*.*"; - wxString filename = wxFileSelector(_("Save Timecodes File"),path,"","",str,wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + agi::fs::path filename = wxFileSelector(_("Save Timecodes File"),path,"","",str,wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (!filename.empty()) { c->videoController->SaveTimecodes(filename); - OPT_SET("Path/Last/Timecodes")->SetString(from_wx(wxFileName(filename).GetPath())); + OPT_SET("Path/Last/Timecodes")->SetString(filename.parent_path().string()); } } }; diff --git a/aegisub/src/command/tool.cpp b/aegisub/src/command/tool.cpp index 874e25455..7a8691c03 100644 --- a/aegisub/src/command/tool.cpp +++ b/aegisub/src/command/tool.cpp @@ -36,9 +36,6 @@ #include "../config.h" -#include -#include - #include "command.h" #include "../compat.h" @@ -56,6 +53,11 @@ #include "../standard_paths.h" #include "../video_context.h" +#include + +#include +#include + namespace { using cmd::Command; /// @defgroup cmd-tool Various tool and utilities @@ -70,7 +72,7 @@ struct tool_assdraw : public Command { STR_HELP("Launch ASSDraw3 tool for vector drawing") void operator()(agi::Context *) { - wxExecute("\"" + StandardPaths::DecodePath("?data/ASSDraw3.exe") + "\""); + wxExecute("\"" + StandardPaths::DecodePath("?data/ASSDraw3.exe").wstring() + "\""); } }; @@ -87,7 +89,6 @@ struct tool_export : public Command { } }; - /// Open fonts collector. struct tool_font_collector : public Command { CMD_NAME("tool/font_collector") @@ -100,7 +101,6 @@ struct tool_font_collector : public Command { } }; - /// Selects lines based on defined criteria. struct tool_line_select : public Command { CMD_NAME("tool/line/select") @@ -113,7 +113,6 @@ struct tool_line_select : public Command { } }; - /// Changes resolution and modifies subtitles to conform to change. struct tool_resampleres : public Command { CMD_NAME("tool/resampleres") @@ -129,7 +128,6 @@ struct tool_resampleres : public Command { } }; - /// Open styling assistant. struct tool_style_assistant : public Command { CMD_NAME("tool/style/assistant") @@ -186,7 +184,6 @@ struct tool_style_manager : public Command { } }; - /// Open Kanji timer. struct tool_time_kanji : public Command { CMD_NAME("tool/time/kanji") @@ -199,7 +196,6 @@ struct tool_time_kanji : public Command { } }; - /// Launch timing post-processor. struct tool_time_postprocess : public Command { CMD_NAME("tool/time/postprocess") @@ -212,7 +208,6 @@ struct tool_time_postprocess : public Command { } }; - /// Open translation assistant. struct tool_translation_assistant : public Command { CMD_NAME("tool/translation_assistant") @@ -314,10 +309,9 @@ namespace cmd { reg(new tool_time_kanji); reg(new tool_time_postprocess); reg(new tool_translation_assistant); -#ifdef __WINDOWS__ - if (wxFileName::FileExists(StandardPaths::DecodePath("?data/ASSDraw3.exe"))) { +#ifdef _WIN32 + if (agi::fs::FileExists(StandardPaths::DecodePath("?data/ASSDraw3.exe"))) reg(new tool_assdraw); - } #endif reg(new tool_translation_assistant_commit); reg(new tool_translation_assistant_preview); diff --git a/aegisub/src/command/video.cpp b/aegisub/src/command/video.cpp index 4850ebdbb..7608e24e6 100644 --- a/aegisub/src/command/video.cpp +++ b/aegisub/src/command/video.cpp @@ -38,12 +38,6 @@ #include "command.h" -#include -#include -#include -#include -#include - #include "../ass_dialogue.h" #include "../ass_time.h" #include "../compat.h" @@ -57,7 +51,6 @@ #include "../main.h" #include "../options.h" #include "../selection_controller.h" -#include "../standard_paths.h" #include "../utils.h" #include "../video_box.h" #include "../video_context.h" @@ -65,6 +58,20 @@ #include "../video_frame.h" #include "../video_slider.h" +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + namespace { using cmd::Command; /// @defgroup cmd-video Video commands. @@ -118,46 +125,30 @@ struct video_aspect_custom : public validator_video_loaded { void operator()(agi::Context *c) { c->videoController->Stop(); - wxString value = wxGetTextFromUser(_("Enter aspect ratio in either:\n decimal (e.g. 2.35)\n fractional (e.g. 16:9)\n specific resolution (e.g. 853x480)"),_("Enter aspect ratio"),AegiFloatToString(c->videoController->GetAspectRatioValue())); - if (value.IsEmpty()) return; + std::string value = from_wx(wxGetTextFromUser( + _("Enter aspect ratio in either:\n decimal (e.g. 2.35)\n fractional (e.g. 16:9)\n specific resolution (e.g. 853x480)"), + _("Enter aspect ratio"), + std::to_wstring(c->videoController->GetAspectRatioValue()))); + if (value.empty()) return; - value.MakeLower(); - - // Process text - double numval; - if (value.ToDouble(&numval)) { + double numval = 0; + if (agi::util::try_parse(value, &numval)) { //Nothing to see here, move along } else { - double a,b; - int pos=0; - bool scale=false; - - //Why bloat using Contains when we can just check the output of Find? - pos = value.Find(':'); - if (pos==wxNOT_FOUND) pos = value.Find('/'); - if (pos==wxNOT_FOUND&&value.Contains('x')) { - pos = value.Find('x'); - scale=true; + std::vector chunks; + split(chunks, value, boost::is_any_of(":/xX")); + if (chunks.size() == 2) { + double num, den; + if (agi::util::try_parse(chunks[0], &num) && agi::util::try_parse(chunks[1], &den)) + numval = num / den; } - - if (pos>0) { - wxString num = value.Left(pos); - wxString denum = value.Mid(pos+1); - if (num.ToDouble(&a) && denum.ToDouble(&b) && b!=0) { - numval = a/b; - if (scale) c->videoDisplay->SetZoom(b / c->videoController->GetHeight()); - } - } - else numval = 0.0; } - // Sanity check - if (numval < 0.5 || numval > 5.0) wxMessageBox(_("Invalid value! Aspect ratio must be between 0.5 and 5.0."),_("Invalid Aspect Ratio"),wxOK | wxICON_ERROR | wxCENTER); - - // Set value + if (numval < 0.5 || numval > 5.0) + wxMessageBox(_("Invalid value! Aspect ratio must be between 0.5 and 5.0."),_("Invalid Aspect Ratio"),wxOK | wxICON_ERROR | wxCENTER); else { - c->videoController->SetAspectRatio(4,numval); + c->videoController->SetAspectRatio(4, numval); wxGetApp().frame->SetDisplayMode(1,-1); } } @@ -473,19 +464,18 @@ struct video_frame_prev_large : public validator_video_loaded { static void save_snapshot(agi::Context *c, bool raw) { static const agi::OptionValue* ssPath = OPT_GET("Path/Screenshot"); - wxString option = to_wx(ssPath->GetString()); - wxFileName videoFile(c->videoController->GetVideoName()); - wxString basepath; + std::string option = ssPath->GetString(); + agi::fs::path basepath; // Is it a path specifier and not an actual fixed path? if (option[0] == '?') { // If dummy video is loaded, we can't save to the video location - if (option.StartsWith("?video") && (c->videoController->GetVideoName().Find("?dummy") != wxNOT_FOUND)) { + if (boost::starts_with(option, "?video") && boost::starts_with(c->videoController->GetVideoName().string(), "?dummy")) { // So try the script location instead option = "?script"; } // Find out where the ?specifier points to - basepath = StandardPaths::DecodePath(option); + basepath = config::path->Decode(option); // If where ever that is isn't defined, we can't save there if ((basepath == "\\") || (basepath == "/")) { // So save to the current user's home dir instead @@ -493,17 +483,18 @@ static void save_snapshot(agi::Context *c, bool raw) { } } // Actual fixed (possibly relative) path, decode it - else basepath = DecodeRelativePath(option,StandardPaths::DecodePath("?user/")); - basepath += "/" + videoFile.GetName(); + else + basepath = config::path->MakeAbsolute(option, "?user/"); + basepath /= c->videoController->GetVideoName().stem(); // Get full path int session_shot_count = 1; - wxString path; + std::string path; do { - path = wxString::Format("%s_%03d_%d.png", basepath, session_shot_count++, c->videoController->GetFrameN()); - } while (wxFileName::FileExists(path)); + path = str(boost::format("%s_%03d_%d.png") % basepath % session_shot_count++ % c->videoController->GetFrameN()); + } while (agi::fs::FileExists(path)); - c->videoController->GetFrame(c->videoController->GetFrameN(), raw)->GetImage().SaveFile(path,wxBITMAP_TYPE_PNG); + c->videoController->GetFrame(c->videoController->GetFrameN(), raw)->GetImage().SaveFile(to_wx(path), wxBITMAP_TYPE_PNG); } /// Save the current video frame, with subtitles (if any) @@ -585,10 +576,10 @@ struct video_open : public Command { wxString path = to_wx(OPT_GET("Path/Last/Video")->GetString()); wxString str = _("Video Formats") + " (*.asf,*.avi,*.avs,*.d2v,*.m2ts,*.m4v,*.mkv,*.mov,*.mp4,*.mpeg,*.mpg,*.ogm,*.webm,*.wmv,*.ts,*.y4m,*.yuv)|*.asf;*.avi;*.avs;*.d2v;*.m2ts;*.m4v;*.mkv;*.mov;*.mp4;*.mpeg;*.mpg;*.ogm;*.webm;*.wmv;*.ts;*.y4m;*.yuv|" + _("All Files") + " (*.*)|*.*"; - wxString filename = wxFileSelector(_("Open video file"),path,"","",str,wxFD_OPEN | wxFD_FILE_MUST_EXIST); + agi::fs::path filename = wxFileSelector(_("Open video file"),path,"","",str,wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (!filename.empty()) { c->videoController->SetVideo(filename); - OPT_SET("Path/Last/Video")->SetString(from_wx(wxFileName(filename).GetPath())); + OPT_SET("Path/Last/Video")->SetString(filename.parent_path().string()); } } }; @@ -601,7 +592,7 @@ struct video_open_dummy : public Command { STR_HELP("Opens a video clip with solid color") void operator()(agi::Context *c) { - wxString fn = DialogDummyVideo::CreateDummyVideo(c->parent); + std::string fn = DialogDummyVideo::CreateDummyVideo(c->parent); if (!fn.empty()) c->videoController->SetVideo(fn); } diff --git a/aegisub/src/compat.cpp b/aegisub/src/compat.cpp index c6b0bf96d..7ea800e1d 100644 --- a/aegisub/src/compat.cpp +++ b/aegisub/src/compat.cpp @@ -1,20 +1,21 @@ #include "compat.h" + #include "options.h" #include -template -wxArrayString to_wxAS(T const& src) { +wxArrayString lagi_MRU_wxAS(std::string const& list) { + auto const& vec = *config::mru->Get(list); wxArrayString ret; - ret.reserve(src.size()); - transform(src.begin(), src.end(), std::back_inserter(ret), (wxString (*)(std::string const&))to_wx); + ret.reserve(vec.size()); + transform(vec.begin(), vec.end(), std::back_inserter(ret), + [](agi::fs::path const& p) { return p.wstring(); }); return ret; } -wxArrayString lagi_MRU_wxAS(const wxString &list) { - return to_wxAS(*config::mru->Get(from_wx(list))); -} - wxArrayString to_wx(std::vector const& vec) { - return to_wxAS(vec); + wxArrayString ret; + ret.reserve(vec.size()); + transform(vec.begin(), vec.end(), std::back_inserter(ret), (wxString (*)(std::string const&))to_wx); + return ret; } diff --git a/aegisub/src/compat.h b/aegisub/src/compat.h index b4a179aba..3d34e1e94 100644 --- a/aegisub/src/compat.h +++ b/aegisub/src/compat.h @@ -1,3 +1,5 @@ +#pragma once + #include #include @@ -14,4 +16,4 @@ wxArrayString to_wx(std::vector const& vec); inline agi::Color from_wx(wxColour color) { return agi::Color(color.Red(), color.Green(), color.Blue(), 255 - color.Alpha()); } inline std::string from_wx(wxString const& str) { return std::string(str.utf8_str()); } -wxArrayString lagi_MRU_wxAS(const wxString &list); +wxArrayString lagi_MRU_wxAS(std::string const& list); diff --git a/aegisub/src/dialog_attachments.cpp b/aegisub/src/dialog_attachments.cpp index 920078f40..dbb7dd924 100644 --- a/aegisub/src/dialog_attachments.cpp +++ b/aegisub/src/dialog_attachments.cpp @@ -39,7 +39,6 @@ #include #include #include -#include #include #include @@ -53,95 +52,70 @@ #include -enum { - BUTTON_ATTACH_FONT = 1300, - BUTTON_ATTACH_GRAPHICS, - BUTTON_EXTRACT, - BUTTON_DELETE, - ATTACHMENT_LIST -}; - DialogAttachments::DialogAttachments(wxWindow *parent, AssFile *ass) -: wxDialog(parent,-1,_("Attachment List"),wxDefaultPosition,wxDefaultSize,wxDEFAULT_DIALOG_STYLE) +: wxDialog(parent, -1, _("Attachment List"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) , ass(ass) { SetIcon(GETICON(attach_button_16)); - listView = new wxListView(this,ATTACHMENT_LIST,wxDefaultPosition,wxSize(500,200)); + listView = new wxListView(this, -1, wxDefaultPosition, wxSize(500, 200)); UpdateList(); - // Buttons - extractButton = new wxButton(this,BUTTON_EXTRACT,_("E&xtract")); - deleteButton = new wxButton(this,BUTTON_DELETE,_("&Delete")); + auto attachFont = new wxButton(this, -1, _("Attach &Font")); + auto attachGraphics = new wxButton(this, -1, _("Attach &Graphics")); + extractButton = new wxButton(this, -1, _("E&xtract")); + deleteButton = new wxButton(this, -1, _("&Delete")); extractButton->Enable(false); deleteButton->Enable(false); - // Buttons sizer - wxSizer *buttonSizer = new wxBoxSizer(wxHORIZONTAL); - buttonSizer->Add(new wxButton(this,BUTTON_ATTACH_FONT,_("Attach &Font")),1,0,0); - buttonSizer->Add(new wxButton(this,BUTTON_ATTACH_GRAPHICS,_("Attach &Graphics")),1,0,0); - buttonSizer->Add(extractButton,1,0,0); - buttonSizer->Add(deleteButton,1,0,0); - buttonSizer->Add(new HelpButton(this,"Attachment Manager"),1,wxLEFT,5); - buttonSizer->Add(new wxButton(this,wxID_CANCEL,_("&Close")),1,0,0); + auto buttonSizer = new wxBoxSizer(wxHORIZONTAL); + buttonSizer->Add(attachFont, 1); + buttonSizer->Add(attachGraphics, 1); + buttonSizer->Add(extractButton, 1); + buttonSizer->Add(deleteButton, 1); + buttonSizer->Add(new HelpButton(this, "Attachment Manager"), 1, wxLEFT, 5); + buttonSizer->Add(new wxButton(this, wxID_CANCEL, _("&Close")), 1); - // Main sizer - wxSizer *mainSizer = new wxBoxSizer(wxVERTICAL); - mainSizer->Add(listView,1,wxTOP | wxLEFT | wxRIGHT | wxEXPAND,5); - mainSizer->Add(buttonSizer,0,wxALL | wxEXPAND,5); + auto mainSizer = new wxBoxSizer(wxVERTICAL); + mainSizer->Add(listView, 1, wxTOP | wxLEFT | wxRIGHT | wxEXPAND, 5); + mainSizer->Add(buttonSizer, 0, wxALL | wxEXPAND, 5); SetSizerAndFit(mainSizer); CenterOnParent(); + + attachFont->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogAttachments::OnAttachFont, this); + attachGraphics->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogAttachments::OnAttachGraphics, this); + extractButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogAttachments::OnExtract, this); + deleteButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogAttachments::OnDelete, this); + + listView->Bind(wxEVT_COMMAND_LIST_ITEM_SELECTED, &DialogAttachments::OnListClick, this); + listView->Bind(wxEVT_COMMAND_LIST_ITEM_DESELECTED, &DialogAttachments::OnListClick, this); + listView->Bind(wxEVT_COMMAND_LIST_ITEM_FOCUSED, &DialogAttachments::OnListClick, this); } void DialogAttachments::UpdateList() { listView->ClearAll(); - // Insert list columns listView->InsertColumn(0, _("Attachment name"), wxLIST_FORMAT_LEFT, 280); listView->InsertColumn(1, _("Size"), wxLIST_FORMAT_LEFT, 100); listView->InsertColumn(2, _("Group"), wxLIST_FORMAT_LEFT, 100); - // Fill list for (auto attach : ass->Line | agi::of_type()) { int row = listView->GetItemCount(); - listView->InsertItem(row,attach->GetFileName(true)); - listView->SetItem(row,1,PrettySize(attach->GetSize())); - listView->SetItem(row,2,attach->GroupHeader()); - listView->SetItemPtrData(row,wxPtrToUInt(attach)); + listView->InsertItem(row, to_wx(attach->GetFileName(true))); + listView->SetItem(row, 1, PrettySize(attach->GetSize())); + listView->SetItem(row, 2, to_wx(attach->GroupHeader())); + listView->SetItemPtrData(row, wxPtrToUInt(attach)); } } -BEGIN_EVENT_TABLE(DialogAttachments,wxDialog) - EVT_BUTTON(BUTTON_ATTACH_FONT,DialogAttachments::OnAttachFont) - EVT_BUTTON(BUTTON_ATTACH_GRAPHICS,DialogAttachments::OnAttachGraphics) - EVT_BUTTON(BUTTON_EXTRACT,DialogAttachments::OnExtract) - EVT_BUTTON(BUTTON_DELETE,DialogAttachments::OnDelete) - EVT_LIST_ITEM_SELECTED(ATTACHMENT_LIST,DialogAttachments::OnListClick) - EVT_LIST_ITEM_DESELECTED(ATTACHMENT_LIST,DialogAttachments::OnListClick) - EVT_LIST_ITEM_FOCUSED(ATTACHMENT_LIST,DialogAttachments::OnListClick) -END_EVENT_TABLE() - -void DialogAttachments::AttachFile(wxFileDialog &diag, AssEntryGroup group, wxString const& commit_msg) { +void DialogAttachments::AttachFile(wxFileDialog &diag, wxString const& commit_msg) { if (diag.ShowModal() == wxID_CANCEL) return; - wxArrayString filenames; - diag.GetFilenames(filenames); - wxArrayString paths; diag.GetPaths(paths); - // Create attachments - for (size_t i = 0; i < filenames.size(); ++i) { - AssAttachment *newAttach = new AssAttachment(filenames[i], group); - try { - newAttach->Import(paths[i]); - } - catch (...) { - delete newAttach; - return; - } - ass->InsertLine(newAttach); - } + for (auto const& fn : paths) + ass->InsertAttachment(agi::fs::path(fn)); ass->Commit(commit_msg, AssFile::COMMIT_ATTACHMENT); @@ -154,43 +128,41 @@ void DialogAttachments::OnAttachFont(wxCommandEvent &) { to_wx(OPT_GET("Path/Fonts Collector Destination")->GetString()), "", "Font Files (*.ttf)|*.ttf", wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE); - AttachFile(diag, ENTRY_FONT, _("attach font file")); + AttachFile(diag, _("attach font file")); } void DialogAttachments::OnAttachGraphics(wxCommandEvent &) { wxFileDialog diag(this, _("Choose file to be attached"), "", "", - "Graphic Files (*.bmp,*.gif,*.jpg,*.ico,*.wmf)|*.bmp;*.gif;*.jpg;*.ico;*.wmf", + "Graphic Files (*.bmp, *.gif, *.jpg, *.ico, *.wmf)|*.bmp;*.gif;*.jpg;*.ico;*.wmf", wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE); - AttachFile(diag, ENTRY_GRAPHIC, _("attach graphics file")); + AttachFile(diag, _("attach graphics file")); } void DialogAttachments::OnExtract(wxCommandEvent &) { int i = listView->GetFirstSelected(); if (i == -1) return; - wxString path; + std::string path; bool fullPath = false; // Multiple or single? if (listView->GetNextSelected(i) != -1) - path = wxDirSelector(_("Select the path to save the files to:"),to_wx(OPT_GET("Path/Fonts Collector Destination")->GetString())) + "/"; + path = from_wx(wxDirSelector(_("Select the path to save the files to:"), to_wx(OPT_GET("Path/Fonts Collector Destination")->GetString()))) + "/"; else { - // Default path - wxString defPath = ((AssAttachment*)wxUIntToPtr(listView->GetItemData(i)))->GetFileName(); - path = wxFileSelector( + path = from_wx(wxFileSelector( _("Select the path to save the file to:"), to_wx(OPT_GET("Path/Fonts Collector Destination")->GetString()), - defPath, + to_wx(((AssAttachment*)wxUIntToPtr(listView->GetItemData(i)))->GetFileName()), ".ttf", "Font Files (*.ttf)|*.ttf", wxFD_SAVE | wxFD_OVERWRITE_PROMPT, - this); + this)); fullPath = true; } - if (!path) return; + if (path.empty()) return; // Loop through items in list while (i != -1) { @@ -201,7 +173,7 @@ void DialogAttachments::OnExtract(wxCommandEvent &) { } void DialogAttachments::OnDelete(wxCommandEvent &) { - int i = listView->GetFirstSelected(); + auto i = listView->GetFirstSelected(); if (i == -1) return; while (i != -1) { diff --git a/aegisub/src/dialog_attachments.h b/aegisub/src/dialog_attachments.h index a90288a07..3a444c9bb 100644 --- a/aegisub/src/dialog_attachments.h +++ b/aegisub/src/dialog_attachments.h @@ -39,8 +39,6 @@ class wxListEvent; #include -#include "ass_entry.h" - class DialogAttachments : public wxDialog { AssFile *ass; @@ -55,10 +53,8 @@ class DialogAttachments : public wxDialog { void OnListClick(wxListEvent &event); void UpdateList(); - void AttachFile(wxFileDialog &diag, AssEntryGroup group, wxString const& commit_msg); + void AttachFile(wxFileDialog &diag, wxString const& commit_msg); public: DialogAttachments(wxWindow *parent, AssFile *ass); - - DECLARE_EVENT_TABLE() }; diff --git a/aegisub/src/dialog_automation.cpp b/aegisub/src/dialog_automation.cpp index 33b379823..869d7c15a 100644 --- a/aegisub/src/dialog_automation.cpp +++ b/aegisub/src/dialog_automation.cpp @@ -32,7 +32,6 @@ /// @ingroup secondary_ui /// - #include "config.h" #include "dialog_automation.h" @@ -51,10 +50,10 @@ #include #include -#include #include #include #include +#include using std::placeholders::_1; @@ -147,9 +146,9 @@ void DialogAutomation::RebuildList() void DialogAutomation::SetScriptInfo(int i, Automation4::Script *script) { - list->SetItem(i, 1, script->GetName()); - list->SetItem(i, 2, wxFileName(script->GetFilename()).GetFullName()); - list->SetItem(i, 3, script->GetDescription()); + list->SetItem(i, 1, to_wx(script->GetName())); + list->SetItem(i, 2, script->GetPrettyFilename().wstring()); + list->SetItem(i, 3, to_wx(script->GetDescription())); if (!script->GetLoadedState()) list->SetItemBackgroundColour(i, wxColour(255,128,128)); else @@ -178,10 +177,10 @@ void DialogAutomation::UpdateDisplay() } template -static bool has_file(Container const& c, wxFileName const& fn) +static bool has_file(Container const& c, agi::fs::path const& fn) { return any_of(c.begin(), c.end(), - [&](const Automation4::Script *s) { return fn.SameAs(s->GetFilename()); }); + [&](const Automation4::Script *s) { return fn == s->GetFilename(); }); } void DialogAutomation::OnAdd(wxCommandEvent &) @@ -190,7 +189,7 @@ void DialogAutomation::OnAdd(wxCommandEvent &) _("Add Automation script"), to_wx(OPT_GET("Path/Last/Automation")->GetString()), "", - Automation4::ScriptFactory::GetWildcardStr(), + to_wx(Automation4::ScriptFactory::GetWildcardStr()), wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE); if (diag.ShowModal() == wxID_CANCEL) return; @@ -199,15 +198,15 @@ void DialogAutomation::OnAdd(wxCommandEvent &) diag.GetPaths(fnames); for (auto const& fname : fnames) { - wxFileName fnpath(fname); - OPT_SET("Path/Last/Automation")->SetString(from_wx(fnpath.GetPath())); + agi::fs::path fnpath(fname); + OPT_SET("Path/Last/Automation")->SetString(fnpath.parent_path().string()); if (has_file(local_manager->GetScripts(), fnpath) || has_file(global_manager->GetScripts(), fnpath)) { wxLogError("Script '%s' is already loaded", fname); continue; } - local_manager->Add(Automation4::ScriptFactory::CreateFromFile(fname, true)); + local_manager->Add(Automation4::ScriptFactory::CreateFromFile(fnpath, true)); } } @@ -234,19 +233,19 @@ void DialogAutomation::OnReload(wxCommandEvent &) } static wxString fac_to_str(const Automation4::ScriptFactory* f) { - return wxString::Format("- %s (%s)", f->GetEngineName(), f->GetFilenamePattern()); + return wxString::Format("- %s (%s)", to_wx(f->GetEngineName()), to_wx(f->GetFilenamePattern())); } static wxString cmd_to_str(const cmd::Command *f, agi::Context *c) { - return wxString::Format(_(" Macro: %s (%s)"), f->StrDisplay(c), f->name()); + return wxString::Format(_(" Macro: %s (%s)"), f->StrDisplay(c), to_wx(f->name())); } static wxString filt_to_str(const Automation4::ExportFilter* f) { - return wxString::Format(_(" Export filter: %s"), f->GetName()); + return wxString::Format(_(" Export filter: %s"), to_wx(f->GetName())); } static wxString form_to_str(const SubtitleFormat* f) { - return wxString::Format(_(" Subtitle format handler: %s"), f->GetName()); + return wxString::Format(_(" Subtitle format handler: %s"), to_wx(f->GetName())); } void DialogAutomation::OnInfo(wxCommandEvent &) @@ -272,7 +271,7 @@ void DialogAutomation::OnInfo(wxCommandEvent &) ei->script->GetDescription(), ei->script->GetAuthor(), ei->script->GetVersion(), - ei->script->GetFilename(), + ei->script->GetFilename().wstring(), ei->script->GetLoadedState() ? _("Correctly loaded") : _("Failed to load"))); transform(ei->script->GetMacros(), append_info, std::bind(cmd_to_str, _1, context)); diff --git a/aegisub/src/dialog_autosave.cpp b/aegisub/src/dialog_autosave.cpp index 75beee9b1..5808ea225 100644 --- a/aegisub/src/dialog_autosave.cpp +++ b/aegisub/src/dialog_autosave.cpp @@ -90,7 +90,7 @@ DialogAutosave::DialogAutosave(wxWindow *parent) } void DialogAutosave::Populate(std::map &files_map, std::string const& path, wxString const& filter, wxString const& name_fmt) { - wxString directory(StandardPaths::DecodePath(to_wx(path))); + wxString directory(StandardPaths::DecodePath(path).wstring()); wxDir dir; if (!dir.Open(directory)) return; @@ -130,12 +130,12 @@ void DialogAutosave::OnSelectFile(wxCommandEvent&) { version_list->SetSelection(0); } -wxString DialogAutosave::ChosenFile() const { +std::string DialogAutosave::ChosenFile() const { int sel_file = file_list->GetSelection(); if (sel_file < 0) return ""; int sel_version = version_list->GetSelection(); if (sel_version < 0) return ""; - return files[sel_file].versions[sel_version].filename; + return from_wx(files[sel_file].versions[sel_version].filename); } diff --git a/aegisub/src/dialog_autosave.h b/aegisub/src/dialog_autosave.h index 4e9b9d15b..3d0187ba0 100644 --- a/aegisub/src/dialog_autosave.h +++ b/aegisub/src/dialog_autosave.h @@ -49,5 +49,5 @@ class DialogAutosave : public wxDialog { public: DialogAutosave(wxWindow *parent); - wxString ChosenFile() const; + std::string ChosenFile() const; }; diff --git a/aegisub/src/dialog_detached_video.cpp b/aegisub/src/dialog_detached_video.cpp index 0a64683ee..0291a2690 100644 --- a/aegisub/src/dialog_detached_video.cpp +++ b/aegisub/src/dialog_detached_video.cpp @@ -38,7 +38,6 @@ #include "include/aegisub/context.h" #include "include/aegisub/hotkey.h" - #include "options.h" #include "persist_location.h" #include "utils.h" @@ -46,7 +45,8 @@ #include "video_context.h" #include "video_display.h" -#include +#include + #include #include /// Must be included last. @@ -60,7 +60,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"), wxFileName(context->videoController->GetVideoName()).GetFullName())); + SetTitle(wxString::Format(_("Video: %s"), context->videoController->GetVideoName().filename().wstring())); old_display->Unload(); diff --git a/aegisub/src/dialog_dummy_video.cpp b/aegisub/src/dialog_dummy_video.cpp index 151982ae4..09c45fba1 100644 --- a/aegisub/src/dialog_dummy_video.cpp +++ b/aegisub/src/dialog_dummy_video.cpp @@ -158,7 +158,7 @@ void DialogDummyVideo::UpdateLengthDisplay() { length_display->SetLabel(wxString::Format(_("Resulting duration: %s"), AssTime(length / fps * 1000).GetAssFormated(true))); } -wxString DialogDummyVideo::CreateDummyVideo(wxWindow *parent) { +std::string DialogDummyVideo::CreateDummyVideo(wxWindow *parent) { DialogDummyVideo dlg(parent); if (dlg.ShowModal() != wxID_OK) return ""; diff --git a/aegisub/src/dialog_dummy_video.h b/aegisub/src/dialog_dummy_video.h index 4a06d1bd4..667d49045 100644 --- a/aegisub/src/dialog_dummy_video.h +++ b/aegisub/src/dialog_dummy_video.h @@ -46,5 +46,5 @@ class DialogDummyVideo : public wxDialog { void UpdateLengthDisplay(); public: - static wxString CreateDummyVideo(wxWindow *parent); + static std::string CreateDummyVideo(wxWindow *parent); }; diff --git a/aegisub/src/dialog_export.cpp b/aegisub/src/dialog_export.cpp index 117a7eefb..61ec52e03 100644 --- a/aegisub/src/dialog_export.cpp +++ b/aegisub/src/dialog_export.cpp @@ -40,11 +40,15 @@ #include "ass_file.h" #include "compat.h" #include "include/aegisub/context.h" -#include "charset_conv.h" #include "help_button.h" #include "libresrc/libresrc.h" #include "subtitle_format.h" +#include + +#include +#include + #include #include #include @@ -53,7 +57,6 @@ #include #include #include -#include // Swap the items at idx and idx + 1 static void swap(wxCheckListBox *list, int idx, int sel_dir) { @@ -78,21 +81,18 @@ DialogExport::DialogExport(agi::Context *c) SetIcon(GETICON(export_menu_16)); SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY); - wxArrayString filters = exporter->GetAllFilterNames(); - filter_list = new wxCheckListBox(this, -1, wxDefaultPosition, wxSize(200, 100), filters); + std::vector filters = exporter->GetAllFilterNames(); + filter_list = new wxCheckListBox(this, -1, wxDefaultPosition, wxSize(200, 100), to_wx(filters)); filter_list->Bind(wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, [=](wxCommandEvent&) { RefreshOptions(); }); filter_list->Bind(wxEVT_COMMAND_LISTBOX_SELECTED, &DialogExport::OnChange, this); // Get selected filters - wxString selected = c->ass->GetScriptInfo("Export filters"); - wxStringTokenizer token(selected, "|"); - while (token.HasMoreTokens()) { - wxString cur = token.GetNextToken(); - if (!cur.empty()) { - int idx = filters.Index(cur); - if (idx != wxNOT_FOUND) - filter_list->Check(idx); - } + std::string selected = c->ass->GetScriptInfo("Export filters"); + boost::char_separator sep("|"); + for (auto const& token : boost::tokenizer>(selected, sep)) { + auto it = find(begin(filters), end(filters), token); + if (it != end(filters)) + filter_list->Check(distance(begin(filters), it)); } wxButton *btn_up = new wxButton(this, -1, _("Move &Up"), wxDefaultPosition, wxSize(90, -1)); @@ -119,7 +119,7 @@ DialogExport::DialogExport(agi::Context *c) wxSizer *charset_list_sizer = new wxBoxSizer(wxHORIZONTAL); charset_list_sizer->Add(charset_list_label, wxSizerFlags().Center().Border(wxRIGHT)); charset_list_sizer->Add(charset_list, wxSizerFlags(1).Expand()); - if (!charset_list->SetStringSelection(c->ass->GetScriptInfo("Export Encoding"))) + if (!charset_list->SetStringSelection(to_wx(c->ass->GetScriptInfo("Export Encoding")))) charset_list->SetStringSelection("Unicode (UTF-8)"); wxSizer *top_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Filters")); @@ -148,30 +148,32 @@ DialogExport::DialogExport(agi::Context *c) } DialogExport::~DialogExport() { - wxString infoList; + std::string infoList; for (size_t i = 0; i < filter_list->GetCount(); ++i) { - if (filter_list->IsChecked(i)) - infoList += filter_list->GetString(i) + "|"; + if (filter_list->IsChecked(i)) { + if (!infoList.empty()) + infoList += "|"; + infoList += from_wx(filter_list->GetString(i)); + } } - if (!infoList.empty()) infoList.RemoveLast(); c->ass->SetScriptInfo("Export filters", infoList); } void DialogExport::OnProcess(wxCommandEvent &) { if (!TransferDataFromWindow()) return; - wxString filename = wxFileSelector(_("Export subtitles file"), "", "", "", SubtitleFormat::GetWildcards(1), wxFD_SAVE | wxFD_OVERWRITE_PROMPT, this); + auto filename = wxFileSelector(_("Export subtitles file"), "", "", "", to_wx(SubtitleFormat::GetWildcards(1)), wxFD_SAVE | wxFD_OVERWRITE_PROMPT, this); if (filename.empty()) return; for (size_t i = 0; i < filter_list->GetCount(); ++i) { if (filter_list->IsChecked(i)) - exporter->AddFilter(filter_list->GetString(i)); + exporter->AddFilter(from_wx(filter_list->GetString(i))); } try { wxBusyCursor busy; - c->ass->SetScriptInfo("Export Encoding", charset_list->GetStringSelection()); - exporter->Export(filename, charset_list->GetStringSelection(), this); + c->ass->SetScriptInfo("Export Encoding", from_wx(charset_list->GetStringSelection())); + exporter->Export(from_wx(filename), from_wx(charset_list->GetStringSelection()), this); } catch (agi::UserCancelException const&) { } @@ -184,6 +186,9 @@ void DialogExport::OnProcess(wxCommandEvent &) { catch (agi::Exception const& err) { wxMessageBox(to_wx(err.GetMessage()), "Error exporting subtitles", wxOK | wxICON_ERROR | wxCENTER, this); } + catch (std::exception const& err) { + wxMessageBox(to_wx(err.what()), "Error exporting subtitles", wxOK | wxICON_ERROR | wxCENTER, this); + } catch (...) { wxMessageBox("Unknown error", "Error exporting subtitles", wxOK | wxICON_ERROR | wxCENTER, this); } @@ -192,11 +197,9 @@ void DialogExport::OnProcess(wxCommandEvent &) { } void DialogExport::OnChange(wxCommandEvent &) { - int n = filter_list->GetSelection(); - if (n != wxNOT_FOUND) { - wxString name = filter_list->GetString(n); - filter_description->SetValue(exporter->GetDescription(name)); - } + wxString sel = filter_list->GetStringSelection(); + if (!sel.empty()) + filter_description->SetValue(to_wx(exporter->GetDescription(from_wx(sel)))); } void DialogExport::SetAll(bool new_value) { @@ -210,7 +213,7 @@ void DialogExport::SetAll(bool new_value) { void DialogExport::RefreshOptions() { for (size_t i = 0; i < filter_list->GetCount(); ++i) { - if (wxSizer *sizer = exporter->GetSettingsSizer(filter_list->GetString(i))) + if (wxSizer *sizer = exporter->GetSettingsSizer(from_wx(filter_list->GetString(i)))) opt_sizer->Show(sizer, filter_list->IsChecked(i), true); } Layout(); diff --git a/aegisub/src/dialog_export_ebu3264.cpp b/aegisub/src/dialog_export_ebu3264.cpp index cfb8b9652..ca833977d 100644 --- a/aegisub/src/dialog_export_ebu3264.cpp +++ b/aegisub/src/dialog_export_ebu3264.cpp @@ -23,16 +23,19 @@ #include "dialog_export_ebu3264.h" -#include - +#include "compat.h" #include "options.h" #include "text_file_writer.h" +#include + +#include +#include + #include #include #include #include -#include #include #include #include @@ -41,11 +44,10 @@ #include namespace { - const char timecode_regex[] = "([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2})"; + const boost::regex timecode_regex("([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2})"); /// Validator for SMPTE timecodes class TimecodeValidator : public wxValidator { - wxRegEx re; EbuTimecode *value; wxTextCtrl *GetCtrl() const { return dynamic_cast(GetWindow()); } @@ -61,29 +63,24 @@ namespace { wxTextCtrl *ctrl = GetCtrl(); if (!ctrl) return false; - wxString str = ctrl->GetValue(); + std::string str = from_wx(ctrl->GetValue()); + boost::smatch result; + if (!regex_match(str, result, timecode_regex)) + return false; - if (re.Matches(str)) { - long h, m, s, f; - re.GetMatch(str, 1).ToLong(&h); - re.GetMatch(str, 2).ToLong(&m); - re.GetMatch(str, 3).ToLong(&s); - re.GetMatch(str, 4).ToLong(&f); - value->h = h; - value->m = m; - value->s = s; - value->f = f; - return true; - } + value->h = boost::lexical_cast(result.str(1)); + value->m = boost::lexical_cast(result.str(2)); + value->s = boost::lexical_cast(result.str(3)); + value->f = boost::lexical_cast(result.str(4)); - return false; + return true; } bool Validate(wxWindow *parent) { wxTextCtrl *ctrl = GetCtrl(); if (!ctrl) return false; - if (!re.Matches(ctrl->GetValue())) { + if (!regex_match(from_wx(ctrl->GetValue()), timecode_regex)) { wxMessageBox(_("Time code offset in incorrect format. Ensure it is entered as four groups of two digits separated by colons."), _("EBU STL export"), wxICON_EXCLAMATION); return false; } @@ -93,19 +90,8 @@ namespace { wxObject *Clone() const { return new TimecodeValidator(*this); } public: - TimecodeValidator(EbuTimecode *target) - : re(timecode_regex) - , value(target) - { - assert(target); - } - - TimecodeValidator(TimecodeValidator const& other) - : wxValidator() - , re(timecode_regex) - , value(other.value) - { - } + TimecodeValidator(EbuTimecode *target) : value(target) { assert(target); } + TimecodeValidator(TimecodeValidator const& other) : value(other.value) { } }; } // namespace { @@ -228,13 +214,13 @@ agi::vfr::Framerate EbuExportSettings::GetFramerate() const { agi::charset::IconvWrapper *EbuExportSettings::GetTextEncoder() const { switch (text_encoding) { - case iso6937_2: return new agi::charset::IconvWrapper(wxSTRING_ENCODING, "ISO-6937-2"); - case iso8859_5: return new agi::charset::IconvWrapper(wxSTRING_ENCODING, "ISO-8859-5"); - case iso8859_6: return new agi::charset::IconvWrapper(wxSTRING_ENCODING, "ISO-8859-6"); - case iso8859_7: return new agi::charset::IconvWrapper(wxSTRING_ENCODING, "ISO-8859-7"); - case iso8859_8: return new agi::charset::IconvWrapper(wxSTRING_ENCODING, "ISO-8859-8"); - case utf8: return new agi::charset::IconvWrapper(wxSTRING_ENCODING, "utf-8"); - default: return new agi::charset::IconvWrapper(wxSTRING_ENCODING, "ISO-8859-1"); + case iso6937_2: return new agi::charset::IconvWrapper("utf-8", "ISO-6937-2"); + case iso8859_5: return new agi::charset::IconvWrapper("utf-8", "ISO-8859-5"); + case iso8859_6: return new agi::charset::IconvWrapper("utf-8", "ISO-8859-6"); + case iso8859_7: return new agi::charset::IconvWrapper("utf-8", "ISO-8859-7"); + case iso8859_8: return new agi::charset::IconvWrapper("utf-8", "ISO-8859-8"); + case utf8: return new agi::charset::IconvWrapper("utf-8", "utf-8"); + default: return new agi::charset::IconvWrapper("utf-8", "ISO-8859-1"); } } diff --git a/aegisub/src/dialog_fonts_collector.cpp b/aegisub/src/dialog_fonts_collector.cpp index 3a39b5199..13db7c8eb 100644 --- a/aegisub/src/dialog_fonts_collector.cpp +++ b/aegisub/src/dialog_fonts_collector.cpp @@ -37,7 +37,8 @@ #include "standard_paths.h" #include "utils.h" -#include +#include +#include #include #include @@ -65,88 +66,95 @@ enum FcMode { wxDEFINE_EVENT(EVT_ADD_TEXT, wxThreadEvent); wxDEFINE_EVENT(EVT_COLLECTION_DONE, wxThreadEvent); -/// @class FontsCollectorThread -/// @brief Worker thread for the font collector dialog -class FontsCollectorThread : public wxThread { - AssFile *subs; ///< Subtitle file to process - wxString destination; ///< Path to write fonts to for modes 2 and 3 - wxEvtHandler *collector; ///< Parent dialog - FcMode oper; ///< Copying mode - - void Collect() { - using namespace std::placeholders; - - FontCollectorStatusCallback callback(std::bind(&FontsCollectorThread::AppendText, this, _1, _2)); +void FontsCollectorThread(AssFile *subs, agi::fs::path const& destination, FcMode oper, wxEvtHandler *collector) { + agi::dispatch::Background().Async([=]{ + auto AppendText = [&](wxString text, int colour) { + wxThreadEvent event(EVT_ADD_TEXT); + event.SetPayload(std::make_pair(colour, text)); + collector->AddPendingEvent(event); + }; #ifdef WITH_FONTCONFIG - FontConfigFontFileLister lister(callback); + FontConfigFontFileLister lister(AppendText); #else AppendText(_("Aegisub was built without any font file listers enabled"), 2); struct DummyLister : public FontFileLister { - CollectionResult GetFontPaths(wxString const&, int, bool, std::set const&) { return CollectionResult(); } + CollectionResult GetFontPaths(std::string const&, int, bool, std::set const&) { return CollectionResult(); } } lister; #endif - std::vector paths = FontCollector(callback, lister).GetFontPaths(subs); - if (paths.empty()) return; + auto paths = FontCollector(AppendText, lister).GetFontPaths(subs); + if (paths.empty()) { + collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE)); + return; + } // Copy fonts switch (oper) { case CheckFontsOnly: + collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE)); return; case SymlinkToFolder: - AppendText(_("Symlinking fonts to folder...\n")); + AppendText(_("Symlinking fonts to folder...\n"), 0); break; case CopyToScriptFolder: case CopyToFolder: - AppendText(_("Copying fonts to folder...\n")); + AppendText(_("Copying fonts to folder...\n"), 0); break; case CopyToZip: - AppendText(_("Copying fonts to archive...\n")); + AppendText(_("Copying fonts to archive...\n"), 0); break; } // Open zip stream if saving to compressed archive - agi::scoped_ptr out; - agi::scoped_ptr zip; + std::unique_ptr out; + std::unique_ptr zip; if (oper == CopyToZip) { - out.reset(new wxFFileOutputStream(destination)); + out.reset(new wxFFileOutputStream(destination.wstring())); zip.reset(new wxZipOutputStream(*out)); } int64_t total_size = 0; bool allOk = true; - for (wxString const& path : paths) { + for (auto path : paths) { + path.make_preferred(); + int ret = 0; - wxFileName cur_fn(path); - total_size += cur_fn.GetSize().GetValue(); + total_size += agi::fs::Size(path); switch (oper) { case SymlinkToFolder: case CopyToScriptFolder: case CopyToFolder: { - wxString dest = destination + cur_fn.GetFullName(); - if (wxFileName::FileExists(dest)) + auto dest = destination/path.filename(); + if (agi::fs::FileExists(dest)) ret = 2; #ifndef _WIN32 else if (oper == SymlinkToFolder) { // returns 0 on success, -1 on error... - if (symlink(cur_fn.GetFullPath().utf8_str(), dest.utf8_str())) + if (symlink(path.c_str(), dest.c_str())) ret = 0; else ret = 3; } #endif - else - ret = wxCopyFile(path, dest, true); + else { + try { + agi::fs::Copy(path, dest); + ret = true; + } + catch (...) { + ret = false; + } + } } break; case CopyToZip: { - wxFFileInputStream in(path); + wxFFileInputStream in(path.wstring()); if (!in.IsOk()) ret = false; else { - ret = zip->PutNextEntry(cur_fn.GetFullName()); + ret = zip->PutNextEntry(path.filename().wstring()); zip->Write(in); } } @@ -154,13 +162,13 @@ class FontsCollectorThread : public wxThread { } if (ret == 1) - AppendText(wxString::Format(_("* Copied %s.\n"), path), 1); + AppendText(wxString::Format(_("* Copied %s.\n"), path.wstring()), 1); else if (ret == 2) - AppendText(wxString::Format(_("* %s already exists on destination.\n"), wxFileName(path).GetFullName()), 3); + AppendText(wxString::Format(_("* %s already exists on destination.\n"), path.filename().wstring()), 3); else if (ret == 3) - AppendText(wxString::Format(_("* Symlinked %s.\n"), path), 1); + AppendText(wxString::Format(_("* Symlinked %s.\n"), path.wstring()), 1); else { - AppendText(wxString::Format(_("* Failed to copy %s.\n"), path), 2); + AppendText(wxString::Format(_("* Failed to copy %s.\n"), path.wstring()), 2); allOk = false; } } @@ -173,36 +181,11 @@ class FontsCollectorThread : public wxThread { if (total_size > 32 * 1024 * 1024) AppendText(_("\nOver 32 MB of fonts were copied. Some of the fonts may not be loaded by the player if they are all attached to a Matroska file."), 2); - AppendText("\n"); - } + AppendText("\n", 0); - /// @brief Tell the dialog to add text to the textbox - /// @param text Text to add - /// @param colour 0: neutral; 1: success; 2: error - void AppendText(wxString text, int colour=0) { - wxThreadEvent event(EVT_ADD_TEXT); - event.SetPayload(std::make_pair(colour, text)); - collector->AddPendingEvent(event); - } - - /// Do the font collection - wxThread::ExitCode Entry() { - Collect(); collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE)); - return 0; - } -public: - FontsCollectorThread(AssFile *subs, wxString destination, FcMode oper, wxEvtHandler *collector) - : wxThread(wxTHREAD_DETACHED) - , subs(subs) - , destination(destination) - , collector(collector) - , oper(oper) - { - Create(); - Run(); - } -}; + }); +} DialogFontsCollector::DialogFontsCollector(agi::Context *c) : wxDialog(c->parent, -1, _("Fonts Collector")) @@ -222,13 +205,13 @@ DialogFontsCollector::DialogFontsCollector(agi::Context *c) collection_mode = new wxRadioBox(this, -1, _("Action"), wxDefaultPosition, wxDefaultSize, countof(modes), modes, 1); collection_mode->SetSelection(mid(0, OPT_GET("Tool/Fonts Collector/Action")->GetInt(), 4)); - if (!subs->filename) + if (StandardPaths::DecodePath("?script") == "?script") collection_mode->Enable(2, false); wxStaticBoxSizer *destination_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Destination")); dest_label = new wxStaticText(this, -1, " "); - dest_ctrl = new wxTextCtrl(this, -1, StandardPaths::DecodePath(to_wx(OPT_GET("Path/Fonts Collector Destination")->GetString()))); + dest_ctrl = new wxTextCtrl(this, -1, StandardPaths::DecodePath(OPT_GET("Path/Fonts Collector Destination")->GetString()).wstring()); dest_browse_button = new wxButton(this, -1, _("&Browse...")); wxSizer *dest_browse_sizer = new wxBoxSizer(wxHORIZONTAL); @@ -280,40 +263,31 @@ void DialogFontsCollector::OnStart(wxCommandEvent &) { collection_log->ClearAll(); collection_log->SetReadOnly(true); - wxString dest; + agi::fs::path dest; int action = collection_mode->GetSelection(); OPT_SET("Tool/Fonts Collector/Action")->SetInt(action); if (action != CheckFontsOnly) { - if (action == CopyToScriptFolder) - dest = "?script/"; - else - dest = dest_ctrl->GetValue(); - dest = StandardPaths::DecodePath(dest); - wxFileName folder = dest; + dest = StandardPaths::DecodePath(action == CopyToScriptFolder ? "?script/" : from_wx(dest_ctrl->GetValue())); if (action != CopyToZip) { - if (dest.Last() != '/' && dest.Last() != '\\') { - dest += wxFileName::GetPathSeparator(); - folder = dest; - } - - if (folder.FileExists()) + if (agi::fs::FileExists(dest)) wxMessageBox(_("Invalid destination."), _("Error"), wxOK | wxICON_ERROR | wxCENTER, this); - if (!folder.DirExists()) - folder.Mkdir(0777, wxPATH_MKDIR_FULL); - if (!folder.DirExists()) { + try { + agi::fs::CreateDirectory(dest); + } + catch (agi::Exception const&) { wxMessageBox(_("Could not create destination folder."), _("Error"), wxOK | wxICON_ERROR | wxCENTER, this); return; } } - else if (folder.IsDir() || folder.GetName().empty()) { + else if (agi::fs::DirectoryExists(dest) || dest.filename().empty()) { wxMessageBox(_("Invalid path for .zip file."), _("Error"), wxOK | wxICON_ERROR | wxCENTER, this); return; } } if (action != CheckFontsOnly) - OPT_SET("Path/Fonts Collector Destination")->SetString(from_wx(dest)); + OPT_SET("Path/Fonts Collector Destination")->SetString(dest.string()); // Disable the UI while it runs as we don't support canceling EnableCloseButton(false); @@ -324,7 +298,7 @@ void DialogFontsCollector::OnStart(wxCommandEvent &) { collection_mode->Enable(false); dest_label->Enable(false); - new FontsCollectorThread(subs, dest, static_cast(action), GetEventHandler()); + FontsCollectorThread(subs, dest, static_cast(action), GetEventHandler()); } void DialogFontsCollector::OnBrowse(wxCommandEvent &) { @@ -397,7 +371,7 @@ void DialogFontsCollector::OnCollectionComplete(wxThreadEvent &) { start_btn->Enable(); close_btn->Enable(); collection_mode->Enable(); - if (!subs->filename) + if (StandardPaths::DecodePath("?script") == "?script") collection_mode->Enable(2, false); wxCommandEvent evt; diff --git a/aegisub/src/dialog_jumpto.h b/aegisub/src/dialog_jumpto.h index e5c0fa443..026bc0027 100644 --- a/aegisub/src/dialog_jumpto.h +++ b/aegisub/src/dialog_jumpto.h @@ -32,8 +32,9 @@ /// @ingroup secondary_ui /// -class TimeEdit; namespace agi { struct Context; } +class TimeEdit; +class wxTextCtrl; class DialogJumpTo : public wxDialog { agi::Context *c; ///< Project context diff --git a/aegisub/src/dialog_kara_timing_copy.cpp b/aegisub/src/dialog_kara_timing_copy.cpp index f9d26abb3..9ec86c4cf 100644 --- a/aegisub/src/dialog_kara_timing_copy.cpp +++ b/aegisub/src/dialog_kara_timing_copy.cpp @@ -37,19 +37,6 @@ #include "dialog_kara_timing_copy.h" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "ass_dialogue.h" #include "ass_file.h" #include "ass_karaoke.h" @@ -62,6 +49,18 @@ #include "selection_controller.h" #include "utils.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + #define TEXT_LABEL_SOURCE _("Source: ") #define TEXT_LABEL_DEST _("Dest: ") @@ -81,14 +80,14 @@ class KaraokeLineMatchDisplay : public wxControl { struct MatchGroup { std::vector src; - wxString dst; + std::string dst; int last_render_width; MatchGroup() : last_render_width(0) { } }; std::vector matched_groups; std::deque unmatched_source; - wxString unmatched_destination; + std::string unmatched_destination; int last_total_matchgroup_render_width; @@ -103,7 +102,7 @@ public: /// Start processing a new line pair void SetInputData(AssDialogue *src, AssDialogue *dst); /// Build and return the output line from the matched syllables - wxString GetOutputLine() const; + std::string GetOutputLine() const; /// Number of syllables not yet matched from source size_t GetRemainingSource() const { return unmatched_source.size(); } @@ -159,7 +158,7 @@ wxSize KaraokeLineMatchDisplay::GetBestSize() const return wxSize(min_width * 2, h_src + h_dst + 7); } -int DrawBoxedText(wxDC &dc, const wxString &txt, int x, int y) +int DrawBoxedText(wxDC &dc, const std::string &txt, int x, int y) { int tw, th; // Assume the pen, brush and font properties have already been set in the DC. @@ -176,9 +175,10 @@ int DrawBoxedText(wxDC &dc, const wxString &txt, int x, int y) } else { - dc.GetTextExtent(txt, &tw, &th); + wxString wxtxt(to_wx(txt)); + dc.GetTextExtent(wxtxt, &tw, &th); dc.DrawRectangle(x, y-2, tw+4, th+4); - dc.DrawText(txt, x+2, y); + dc.DrawText(wxtxt, x+2, y); return tw+3; } } @@ -267,7 +267,7 @@ void KaraokeLineMatchDisplay::OnPaint(wxPaintEvent &) // Matched source syllables int syl_x = next_x; for (auto const& syl : grp.src) - syl_x += DrawBoxedText(dc, to_wx(syl.text), syl_x, y_line1); + syl_x += DrawBoxedText(dc, syl.text, syl_x, y_line1); // Matched destination text { @@ -306,11 +306,11 @@ void KaraokeLineMatchDisplay::OnPaint(wxPaintEvent &) dc.SetBrush(wxBrush(inner_back)); } - syl_x += DrawBoxedText(dc, to_wx(unmatched_source[j].text), syl_x, y_line1); + syl_x += DrawBoxedText(dc, unmatched_source[j].text, syl_x, y_line1); } // Remaining destination - wxString txt = unmatched_destination.Left(destination_sel_length); + std::string txt = unmatched_destination.substr(0, destination_sel_length); if (!txt.empty()) { dc.SetTextBackground(sel_back); @@ -319,7 +319,7 @@ void KaraokeLineMatchDisplay::OnPaint(wxPaintEvent &) next_x += DrawBoxedText(dc, txt, next_x, y_line2); } - txt = unmatched_destination.Mid(destination_sel_length); + txt = unmatched_destination.substr(destination_sel_length); if (!txt.empty()) { dc.SetTextBackground(inner_back); @@ -344,30 +344,22 @@ void KaraokeLineMatchDisplay::SetInputData(AssDialogue *src, AssDialogue *dst) source_sel_length = 1; } - unmatched_destination.clear(); - destination_sel_length = 0; - if (dst) - { - unmatched_destination = dst->GetStrippedText(); - if (!unmatched_destination.empty()) - { - destination_sel_length = 1; - } - } + unmatched_destination = dst ? dst->GetStrippedText() : ""; + destination_sel_length = std::max(1, unmatched_destination.size()); Refresh(true); } -wxString KaraokeLineMatchDisplay::GetOutputLine() const +std::string KaraokeLineMatchDisplay::GetOutputLine() const { - wxString res; + std::string res; for (auto const& match : matched_groups) { int duration = 0; for (auto const& syl : match.src) duration += syl.duration; - res = wxString::Format("%s{\\k%d}%s", res, duration / 10, match.dst); + res += "{\\k" + std::to_string(duration / 10) + "}" + match.dst; } return res; @@ -415,15 +407,12 @@ void KaraokeLineMatchDisplay::AutoMatchJapanese() // We'll first see if we can do something with the first unmatched source syllable wxString src(to_wx(unmatched_source[0].text).Lower()); - wxString dst(unmatched_destination); + wxString dst(to_wx(unmatched_destination)); source_sel_length = 1; // we're working on the first, assume it was matched destination_sel_length = 0; // Quick escape: If the source syllable is empty, return with first source syllable and empty destination - if (src.empty()) - { - return; - } + if (src.empty()) return; // Try to match the next source syllable against the destination. Do it // "inverted": try all kana from the table and prefix-match them against @@ -456,19 +445,16 @@ void KaraokeLineMatchDisplay::AutoMatchJapanese() // The source might be empty now: That's good! // That means we managed to match it all against destination text - if (src.empty()) - { - // destination_sel_length already has the appropriate value - // and source_sel_length was alredy 1 - return; - } + if (src.empty()) return; + // destination_sel_length already has the appropriate value + // and source_sel_length was already 1 // Now the source syllable might consist of just whitespace. // Eat all whitespace at the start of the destination. if (StringEmptyOrWhitespace(src)) { - while (destination_sel_length < unmatched_destination.size() && IsWhitespace(unmatched_destination[destination_sel_length])) - ++destination_sel_length; + wxString str(to_wx(unmatched_destination.substr(destination_sel_length))); + destination_sel_length += std::distance(str.begin(), std::find_if_not(str.begin(), str.end(), IsWhitespace)); // Now we've eaten all spaces in the destination as well // so the selection lengths should be good return; @@ -497,7 +483,7 @@ void KaraokeLineMatchDisplay::AutoMatchJapanese() // syllables, not the middle of them. // If a match is found, make a guess at how much source and destination // should be selected based on the distances it was found at. - dst = unmatched_destination; + dst = to_wx(unmatched_destination); for (size_t lookahead = 0; lookahead < KANA_SEARCH_DISTANCE; ++lookahead) { // Eat dst at the beginning, don't test for the first character being kana @@ -567,13 +553,10 @@ void KaraokeLineMatchDisplay::AutoMatchJapanese() bool KaraokeLineMatchDisplay::AcceptMatch() { - MatchGroup match; + // Completely empty match + if (source_sel_length == 0 && destination_sel_length == 0) return false; - if (source_sel_length == 0 && destination_sel_length == 0) - { - // Completely empty match - return false; - } + MatchGroup match; assert(source_sel_length <= unmatched_source.size()); copy(unmatched_source.begin(), unmatched_source.begin() + source_sel_length, back_inserter(match.src)); @@ -581,11 +564,11 @@ bool KaraokeLineMatchDisplay::AcceptMatch() source_sel_length = 0; assert(destination_sel_length <= unmatched_destination.size()); - match.dst = unmatched_destination.Left(destination_sel_length); - unmatched_destination = unmatched_destination.Mid(destination_sel_length); + match.dst = unmatched_destination.substr(0, destination_sel_length); + unmatched_destination.erase(0, destination_sel_length); destination_sel_length = 0; - matched_groups.push_back(match); + matched_groups.emplace_back(std::move(match)); IncreaseSourceMatch(); IncreseDestinationMatch(); @@ -729,8 +712,8 @@ void DialogKanjiTimer::OnStart(wxCommandEvent &) { else if (SourceStyle->GetValue() == DestStyle->GetValue()) wxMessageBox(_("The source and destination styles must be different."),_("Error"),wxICON_EXCLAMATION | wxOK); else { - currentSourceLine = FindNextStyleMatch(&*subs->Line.begin(), SourceStyle->GetValue()); - currentDestinationLine = FindNextStyleMatch(&*subs->Line.begin(), DestStyle->GetValue()); + currentSourceLine = FindNextStyleMatch(&*subs->Line.begin(), from_wx(SourceStyle->GetValue())); + currentDestinationLine = FindNextStyleMatch(&*subs->Line.begin(), from_wx(DestStyle->GetValue())); ResetForNewLine(); } LinesToChange.clear(); @@ -750,12 +733,12 @@ void DialogKanjiTimer::OnUnlink(wxCommandEvent &) { } void DialogKanjiTimer::OnSkipSource(wxCommandEvent &) { - currentSourceLine = FindNextStyleMatch(currentSourceLine, SourceStyle->GetValue()); + currentSourceLine = FindNextStyleMatch(currentSourceLine, from_wx(SourceStyle->GetValue())); ResetForNewLine(); } void DialogKanjiTimer::OnSkipDest(wxCommandEvent &) { - currentDestinationLine = FindNextStyleMatch(currentDestinationLine, DestStyle->GetValue()); + currentDestinationLine = FindNextStyleMatch(currentDestinationLine, from_wx(DestStyle->GetValue())); ResetForNewLine(); } @@ -763,8 +746,8 @@ void DialogKanjiTimer::OnGoBack(wxCommandEvent &) { if (LinesToChange.size()) LinesToChange.pop_back(); //If we go back, then take out the modified line we saved. - currentSourceLine = FindPrevStyleMatch(currentSourceLine, SourceStyle->GetValue()); - currentDestinationLine = FindPrevStyleMatch(currentDestinationLine, DestStyle->GetValue()); + currentSourceLine = FindPrevStyleMatch(currentSourceLine, from_wx(SourceStyle->GetValue())); + currentDestinationLine = FindPrevStyleMatch(currentDestinationLine, from_wx(DestStyle->GetValue())); ResetForNewLine(); } @@ -776,8 +759,8 @@ void DialogKanjiTimer::OnAccept(wxCommandEvent &) { else if (AssDialogue *destLine = dynamic_cast(currentDestinationLine)) { LinesToChange.push_back(std::make_pair(destLine, display->GetOutputLine())); - currentSourceLine = FindNextStyleMatch(currentSourceLine, SourceStyle->GetValue()); - currentDestinationLine = FindNextStyleMatch(currentDestinationLine, DestStyle->GetValue()); + currentSourceLine = FindNextStyleMatch(currentSourceLine, from_wx(SourceStyle->GetValue())); + currentDestinationLine = FindNextStyleMatch(currentDestinationLine, from_wx(DestStyle->GetValue())); ResetForNewLine(); } } @@ -844,7 +827,7 @@ void DialogKanjiTimer::TryAutoMatch() } template -static AssEntry *find_next(Iterator from, Iterator to, wxString const& style_name) { +static AssEntry *find_next(Iterator from, Iterator to, std::string const& style_name) { for (++from; from != to; ++from) { AssDialogue *dlg = dynamic_cast(&*from); @@ -855,13 +838,13 @@ static AssEntry *find_next(Iterator from, Iterator to, wxString const& style_nam return nullptr; } -AssEntry *DialogKanjiTimer::FindNextStyleMatch(AssEntry *search_from, const wxString &search_style) +AssEntry *DialogKanjiTimer::FindNextStyleMatch(AssEntry *search_from, const std::string &search_style) { if (!search_from) return search_from; return find_next(subs->Line.iterator_to(*search_from), subs->Line.end(), search_style); } -AssEntry *DialogKanjiTimer::FindPrevStyleMatch(AssEntry *search_from, const wxString &search_style) +AssEntry *DialogKanjiTimer::FindPrevStyleMatch(AssEntry *search_from, const std::string &search_style) { if (!search_from) return search_from; return find_next(EntryList::reverse_iterator(subs->Line.iterator_to(*search_from)), subs->Line.rend(), search_style); diff --git a/aegisub/src/dialog_kara_timing_copy.h b/aegisub/src/dialog_kara_timing_copy.h index 05cb4bec6..8e1d7cffd 100644 --- a/aegisub/src/dialog_kara_timing_copy.h +++ b/aegisub/src/dialog_kara_timing_copy.h @@ -53,7 +53,7 @@ class DialogKanjiTimer : public wxDialog { wxComboBox *SourceStyle, *DestStyle; wxCheckBox *Interpolate; - std::vector> LinesToChange; + std::vector> LinesToChange; AssEntry *currentSourceLine; AssEntry *currentDestinationLine; @@ -71,8 +71,8 @@ class DialogKanjiTimer : public wxDialog { void ResetForNewLine(); void TryAutoMatch(); - AssEntry *FindNextStyleMatch(AssEntry *search_from, const wxString &search_style); - AssEntry *FindPrevStyleMatch(AssEntry *search_from, const wxString &search_style); + AssEntry *FindNextStyleMatch(AssEntry *search_from, const std::string &search_style); + AssEntry *FindPrevStyleMatch(AssEntry *search_from, const std::string &search_style); public: DialogKanjiTimer(agi::Context *context); diff --git a/aegisub/src/dialog_progress.cpp b/aegisub/src/dialog_progress.cpp index 67c733483..c860edc0a 100644 --- a/aegisub/src/dialog_progress.cpp +++ b/aegisub/src/dialog_progress.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2011, Thomas Goyne +// Copyright (c) 2013, Thomas Goyne // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -21,35 +21,25 @@ #include "dialog_progress.h" -#include - #include "compat.h" #include "utils.h" +#include +#include + +#include #include #include #include #include #include -wxDEFINE_EVENT(EVT_TITLE, wxThreadEvent); -wxDEFINE_EVENT(EVT_MESSAGE, wxThreadEvent); -wxDEFINE_EVENT(EVT_PROGRESS, wxThreadEvent); -wxDEFINE_EVENT(EVT_INDETERMINATE, wxThreadEvent); -wxDEFINE_EVENT(EVT_LOG, wxThreadEvent); -wxDEFINE_EVENT(EVT_COMPLETE, wxThreadEvent); +using agi::dispatch::Main; class DialogProgressSink : public agi::ProgressSink { DialogProgress *dialog; bool cancelled; - wxMutex cancelled_mutex; - - template - void SafeQueue(wxEventType type, T const& value) { - wxThreadEvent *evt = new wxThreadEvent(type); - evt->SetPayload(value); - wxQueueEvent(dialog, evt); - } + std::mutex cancelled_mutex; public: DialogProgressSink(DialogProgress *dialog) @@ -59,62 +49,33 @@ public: } void SetTitle(std::string const& title) { - SafeQueue(EVT_TITLE, to_wx(title)); + Main().Async([=]{ dialog->title->SetLabelText(to_wx(title)); }); } void SetMessage(std::string const& msg) { - SafeQueue(EVT_MESSAGE, to_wx(msg)); + Main().Async([=]{ dialog->text->SetLabelText(to_wx(msg)); }); } void SetProgress(int64_t cur, int64_t max) { - SafeQueue(EVT_PROGRESS, int(double(cur) / max * 100)); + Main().Async([=]{ dialog->gauge->SetValue(mid(0, double(cur) / max * 100, 100)); }); } void Log(std::string const& str) { - SafeQueue(EVT_LOG, to_wx(str)); + Main().Async([=]{ dialog->pending_log += to_wx(str); }); } bool IsCancelled() { - wxMutexLocker l(cancelled_mutex); + std::lock_guard lock(cancelled_mutex); return cancelled; } void Cancel() { - wxMutexLocker l(cancelled_mutex); + std::lock_guard lock(cancelled_mutex); cancelled = true; } void SetIndeterminate() { - wxQueueEvent(dialog, new wxThreadEvent(EVT_INDETERMINATE)); - } -}; - -class TaskRunner : public wxThread { - std::function task; - agi::ProgressSink *ps; - wxDialog *dialog; - -public: - TaskRunner(std::function task, agi::ProgressSink *ps, wxDialog *dialog, int priority) - : task(task) - , ps(ps) - , dialog(dialog) - { - Create(); - if (priority != -1) - SetPriority(priority); - Run(); - } - - wxThread::ExitCode Entry() { - try { - task(ps); - } - catch (agi::Exception const& e) { - ps->Log(e.GetChainedMessage()); - } - wxQueueEvent(dialog, new wxThreadEvent(EVT_COMPLETE)); - return 0; + Main().Async([=]{ dialog->pulse_timer.Start(1000); }); } }; @@ -149,19 +110,38 @@ DialogProgress::DialogProgress(wxWindow *parent, wxString const& title_text, wxS Bind(wxEVT_SHOW, &DialogProgress::OnShow, this); Bind(wxEVT_TIMER, [=](wxTimerEvent&) { gauge->Pulse(); }); Bind(wxEVT_IDLE, &DialogProgress::OnIdle, this); - - Bind(EVT_TITLE, [=](wxThreadEvent& e) { title->SetLabelText(e.GetPayload()); }); - Bind(EVT_MESSAGE, [=](wxThreadEvent& e) { text->SetLabelText(e.GetPayload()); }); - Bind(EVT_PROGRESS, [=](wxThreadEvent& e) { gauge->SetValue(mid(0, e.GetPayload(), 100)); }); - Bind(EVT_INDETERMINATE, [=](wxThreadEvent &) { pulse_timer.Start(1000); }); - Bind(EVT_COMPLETE, &DialogProgress::OnComplete, this); - Bind(EVT_LOG, [=](wxThreadEvent& e) { pending_log += e.GetPayload(); }); } void DialogProgress::Run(std::function task, int priority) { DialogProgressSink ps(this); this->ps = &ps; - new TaskRunner(task, &ps, this, priority); + + agi::dispatch::Background().Async([=]{ + try { + task(this->ps); + } + catch (agi::Exception const& e) { + this->ps->Log(e.GetChainedMessage()); + } + + Main().Async([this]{ + pulse_timer.Stop(); + + // Unbind the cancel handler so that the default behavior happens (i.e. the + // dialog is closed) as there's no longer a task to cancel + Unbind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogProgress::OnCancel, this, wxID_CANCEL); + + // If it ran to completion and there is debug output, leave the window open + // so the user can read the debug output and switch the cancel button to a + // close button + bool cancelled = this->ps->IsCancelled(); + if (cancelled || (log_output->IsEmpty() && !pending_log)) + EndModal(!cancelled); + else + cancel_button->SetLabelText(_("Close")); + }); + }); + if (!ShowModal()) throw agi::UserCancelException("Cancelled by user"); } @@ -197,23 +177,6 @@ void DialogProgress::OnIdle(wxIdleEvent&) { pending_log.clear(); } -void DialogProgress::OnComplete(wxThreadEvent &) { - pulse_timer.Stop(); - - // Unbind the cancel handler so that the default behavior happens (i.e. the - // dialog is closed) as there's no longer a task to cancel - Unbind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogProgress::OnCancel, this, wxID_CANCEL); - - // If it ran to completion and there is debug output, leave the window open - // so the user can read the debug output and switch the cancel button to a - // close button - bool cancelled = ps->IsCancelled(); - if (cancelled || (log_output->IsEmpty() && !pending_log)) - EndModal(!cancelled); - else - cancel_button->SetLabelText(_("Close")); -} - void DialogProgress::OnCancel(wxCommandEvent &) { ps->Cancel(); cancel_button->Enable(false); diff --git a/aegisub/src/dialog_progress.h b/aegisub/src/dialog_progress.h index 9ea462564..52618d855 100644 --- a/aegisub/src/dialog_progress.h +++ b/aegisub/src/dialog_progress.h @@ -21,7 +21,6 @@ #include #include -#include class DialogProgressSink; class wxButton; @@ -32,6 +31,7 @@ class wxTextCtrl; /// @class DialogProgress /// @brief Progress-bar dialog box for displaying during long operations class DialogProgress : public wxDialog, public agi::BackgroundRunner { + friend class DialogProgressSink; DialogProgressSink *ps; wxStaticText *title; @@ -44,8 +44,6 @@ class DialogProgress : public wxDialog, public agi::BackgroundRunner { wxString pending_log; - void OnComplete(wxThreadEvent &evt); - void OnShow(wxShowEvent&); void OnCancel(wxCommandEvent &); void OnIdle(wxIdleEvent&); diff --git a/aegisub/src/dialog_properties.cpp b/aegisub/src/dialog_properties.cpp index f0fb68a1a..f7f18854c 100644 --- a/aegisub/src/dialog_properties.cpp +++ b/aegisub/src/dialog_properties.cpp @@ -34,22 +34,26 @@ #include "config.h" -#include - -#include -#include -#include -#include - #include "dialog_properties.h" #include "ass_file.h" +#include "compat.h" #include "help_button.h" #include "include/aegisub/context.h" #include "libresrc/libresrc.h" #include "validators.h" #include "video_context.h" +#include +#include + +#include +#include +#include +#include +#include +#include + DialogProperties::DialogProperties(agi::Context *c) : wxDialog(c->parent, -1, _("Script Properties")) , c(c) @@ -74,8 +78,8 @@ DialogProperties::DialogProperties(agi::Context *c) // Resolution box wxSizer *ResSizer = new wxStaticBoxSizer(wxHORIZONTAL,this,_("Resolution")); - ResX = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(50,20),0,NumValidator(c->ass->GetScriptInfo("PlayResX"))); - ResY = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(50,20),0,NumValidator(c->ass->GetScriptInfo("PlayResY"))); + ResX = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(50,20),0,NumValidator(to_wx(c->ass->GetScriptInfo("PlayResX")))); + ResY = new wxTextCtrl(this,-1,"",wxDefaultPosition,wxSize(50,20),0,NumValidator(to_wx(c->ass->GetScriptInfo("PlayResY")))); wxStaticText *ResText = new wxStaticText(this,-1,"x"); wxButton *FromVideo = new wxButton(this,-1,_("From &video")); @@ -105,13 +109,13 @@ DialogProperties::DialogProperties(agi::Context *c) wxString coll_opts[] = { _("Normal"), _("Reverse") }; collision = new wxComboBox(this, -1, "", wxDefaultPosition, wxDefaultSize, 2, coll_opts, wxCB_READONLY); - collision->SetSelection(c->ass->GetScriptInfo("Collisions").Lower() == "reverse"); + collision->SetSelection(boost::iequals(c->ass->GetScriptInfo("Collisions"), "reverse")); optionsGrid->Add(new wxStaticText(this,-1,_("Collision: ")),0,wxALIGN_CENTER_VERTICAL,0); optionsGrid->Add(collision,1,wxEXPAND,0); ScaleBorder = new wxCheckBox(this,-1,_("Scale Border and Shadow")); ScaleBorder->SetToolTip(_("Scale border and shadow together with script/render resolution. If this is unchecked, relative border and shadow size will depend on renderer.")); - ScaleBorder->SetValue(c->ass->GetScriptInfo("ScaledBorderAndShadow").Lower() == "yes"); + ScaleBorder->SetValue(boost::iequals(c->ass->GetScriptInfo("ScaledBorderAndShadow"), "yes")); optionsGrid->AddSpacer(0); optionsGrid->Add(ScaleBorder,1,wxEXPAND,0); optionsGrid->AddGrowableCol(1,1); @@ -133,8 +137,8 @@ DialogProperties::DialogProperties(agi::Context *c) CenterOnParent(); } -void DialogProperties::AddProperty(wxSizer *sizer, wxString const& label, wxString const& property) { - wxTextCtrl *ctrl = new wxTextCtrl(this, -1, c->ass->GetScriptInfo(property), wxDefaultPosition, wxSize(200, 20)); +void DialogProperties::AddProperty(wxSizer *sizer, wxString const& label, std::string const& property) { + wxTextCtrl *ctrl = new wxTextCtrl(this, -1, to_wx(c->ass->GetScriptInfo(property)), wxDefaultPosition, wxSize(200, 20)); sizer->Add(new wxStaticText(this, -1, label), wxSizerFlags().Center().Left()); sizer->Add(ctrl, wxSizerFlags(1).Expand()); properties.push_back(std::make_pair(property, ctrl)); @@ -143,21 +147,21 @@ void DialogProperties::AddProperty(wxSizer *sizer, wxString const& label, wxStri void DialogProperties::OnOK(wxCommandEvent &) { int count = 0; for (auto const& prop : properties) - count += SetInfoIfDifferent(prop.first, prop.second->GetValue()); + count += SetInfoIfDifferent(prop.first, from_wx(prop.second->GetValue())); - count += SetInfoIfDifferent("PlayResX", ResX->GetValue()); - count += SetInfoIfDifferent("PlayResY", ResY->GetValue()); - count += SetInfoIfDifferent("WrapStyle", wxString::Format("%d", WrapStyle->GetSelection())); - wxString col[2] = { "Normal", "Reverse" }; + count += SetInfoIfDifferent("PlayResX", from_wx(ResX->GetValue())); + count += SetInfoIfDifferent("PlayResY", from_wx(ResY->GetValue())); + count += SetInfoIfDifferent("WrapStyle", std::to_string(WrapStyle->GetSelection())); + const char *col[2] = { "Normal", "Reverse" }; count += SetInfoIfDifferent("Collisions", col[collision->GetSelection()]); - count += SetInfoIfDifferent("ScaledBorderAndShadow", ScaleBorder->GetValue()? "yes" : "no"); + count += SetInfoIfDifferent("ScaledBorderAndShadow", ScaleBorder->GetValue() ? "yes" : "no"); if (count) c->ass->Commit(_("property changes"), AssFile::COMMIT_SCRIPTINFO); EndModal(!!count); } -int DialogProperties::SetInfoIfDifferent(wxString key,wxString value) { +int DialogProperties::SetInfoIfDifferent(std::string const& key, std::string const&value) { if (c->ass->GetScriptInfo(key) != value) { c->ass->SetScriptInfo(key, value); return 1; diff --git a/aegisub/src/dialog_properties.h b/aegisub/src/dialog_properties.h index 85fee5bfe..608226d33 100644 --- a/aegisub/src/dialog_properties.h +++ b/aegisub/src/dialog_properties.h @@ -33,19 +33,19 @@ /// #include - -#include -#include -#include +#include class AssFile; namespace agi { struct Context; } +class wxCheckBox; +class wxComboBox; +class wxTextCtrl; class DialogProperties : public wxDialog { agi::Context *c; ///< Project this dialog is adjusting the properties of /// Pairs of a script property and a text control for that property - std::vector> properties; + std::vector> properties; // Things that effect rendering wxComboBox *WrapStyle; ///< Wrapping style for long lines @@ -62,13 +62,13 @@ class DialogProperties : public wxDialog { /// @param key Name of field /// @param value New value /// @return Did the value actually need to be changed? - int SetInfoIfDifferent(wxString key, wxString value); + int SetInfoIfDifferent(std::string const& key, std::string const& value); /// Add a property with label and text box for updating the property /// @param sizer Sizer to add the label and control to /// @param label Label text to use /// @param property Script info property name - void AddProperty(wxSizer *sizer, wxString const& label, wxString const& property); + void AddProperty(wxSizer *sizer, wxString const& label, std::string const& property); public: /// Constructor diff --git a/aegisub/src/dialog_resample.cpp b/aegisub/src/dialog_resample.cpp index d0a8f247e..08f70d5da 100644 --- a/aegisub/src/dialog_resample.cpp +++ b/aegisub/src/dialog_resample.cpp @@ -21,16 +21,6 @@ #include "dialog_resample.h" -#include -#include - -#include -#include -#include -#include -#include -#include - #include "ass_dialogue.h" #include "ass_file.h" #include "ass_style.h" @@ -41,6 +31,17 @@ #include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + enum { LEFT = 0, RIGHT = 1, @@ -201,7 +202,7 @@ namespace { void resample_line(resample_state *state, AssEntry &line) { AssDialogue *diag = dynamic_cast(&line); - if (diag && !(diag->Comment && (diag->Effect.get().StartsWith("template") || diag->Effect.get().StartsWith("code")))) { + if (diag && !(diag->Comment && (boost::starts_with(diag->Effect.get(), "template") || boost::starts_with(diag->Effect.get(), "code")))) { boost::ptr_vector blocks(diag->ParseTags()); for (auto block : blocks | agi::of_type()) @@ -248,8 +249,8 @@ void ResampleResolution(AssFile *ass, ResampleSettings const& settings) { for_each(ass->Line.begin(), ass->Line.end(), std::bind(resample_line, &state, std::placeholders::_1)); - ass->SetScriptInfo("PlayResX", wxString::Format("%d", settings.script_x)); - ass->SetScriptInfo("PlayResY", wxString::Format("%d", settings.script_y)); + ass->SetScriptInfo("PlayResX", std::to_string(settings.script_x)); + ass->SetScriptInfo("PlayResY", std::to_string(settings.script_y)); ass->Commit(_("resolution resampling"), AssFile::COMMIT_SCRIPTINFO | AssFile::COMMIT_DIAG_FULL); } diff --git a/aegisub/src/dialog_search_replace.cpp b/aegisub/src/dialog_search_replace.cpp index 2f1aaf1db..a8d932f9c 100644 --- a/aegisub/src/dialog_search_replace.cpp +++ b/aegisub/src/dialog_search_replace.cpp @@ -52,8 +52,8 @@ DialogSearchReplace::DialogSearchReplace(agi::Context* c, bool replace) settings->field = static_cast(OPT_GET("Tool/Search Replace/Field")->GetInt()); settings->limit_to = static_cast(OPT_GET("Tool/Search Replace/Affect")->GetInt()); - settings->find = recent_find.empty() ? wxString() : recent_find.front(); - settings->replace_with = recent_replace.empty() ? wxString() : recent_replace.front(); + settings->find = recent_find.empty() ? std::string() : from_wx(recent_find.front()); + settings->replace_with = recent_replace.empty() ? std::string() : from_wx(recent_replace.front()); settings->match_case = OPT_GET("Tool/Search Replace/Match Case")->GetBool(); settings->use_regex = OPT_GET("Tool/Search Replace/RegExp")->GetBool(); settings->ignore_comments = OPT_GET("Tool/Search Replace/Skip Comments")->GetBool(); @@ -61,12 +61,12 @@ DialogSearchReplace::DialogSearchReplace(agi::Context* c, bool replace) settings->exact_match = false; auto find_sizer = new wxFlexGridSizer(2, 2, 5, 15); - find_edit = new wxComboBox(this, -1, "", wxDefaultPosition, wxSize(300, -1), recent_find, wxCB_DROPDOWN, wxGenericValidator(&settings->find)); + find_edit = new wxComboBox(this, -1, "", wxDefaultPosition, wxSize(300, -1), recent_find, wxCB_DROPDOWN, StringBinder(&settings->find)); find_sizer->Add(new wxStaticText(this, -1, _("Find what:")), wxSizerFlags().Center().Left()); find_sizer->Add(find_edit); if (has_replace) { - replace_edit = new wxComboBox(this, -1, "", wxDefaultPosition, wxSize(300, -1), lagi_MRU_wxAS("Replace"), wxCB_DROPDOWN, wxGenericValidator(&settings->replace_with)); + replace_edit = new wxComboBox(this, -1, "", wxDefaultPosition, wxSize(300, -1), lagi_MRU_wxAS("Replace"), wxCB_DROPDOWN, StringBinder(&settings->replace_with)); find_sizer->Add(new wxStaticText(this, -1, _("Replace with:")), wxSizerFlags().Center().Left()); find_sizer->Add(replace_edit); } @@ -128,11 +128,17 @@ void DialogSearchReplace::FindReplace(bool (SearchReplaceEngine::*func)()) { return; c->search->Configure(*settings); - (c->search->*func)(); + try { + (c->search->*func)(); + } + catch (std::exception const& e) { + wxMessageBox(to_wx(e.what()), "Error", wxOK | wxICON_ERROR | wxCENTER, this); + return; + } - config::mru->Add("Find", from_wx(settings->find)); + config::mru->Add("Find", settings->find); if (has_replace) - config::mru->Add("Replace", from_wx(settings->replace_with)); + config::mru->Add("Replace", settings->replace_with); OPT_SET("Tool/Search Replace/Match Case")->SetBool(settings->match_case); OPT_SET("Tool/Search Replace/RegExp")->SetBool(settings->use_regex); diff --git a/aegisub/src/dialog_selected_choices.cpp b/aegisub/src/dialog_selected_choices.cpp index d354550cf..0705534f2 100644 --- a/aegisub/src/dialog_selected_choices.cpp +++ b/aegisub/src/dialog_selected_choices.cpp @@ -21,6 +21,9 @@ #include "dialog_selected_choices.h" #include +#include +#include +#include SelectedChoicesDialog::SelectedChoicesDialog(wxWindow *parent, wxString const& message, wxString const& caption, wxArrayString const& choices) { Create(parent, message, caption, choices); diff --git a/aegisub/src/dialog_selected_choices.h b/aegisub/src/dialog_selected_choices.h index 6bb869a0f..7671b86e5 100644 --- a/aegisub/src/dialog_selected_choices.h +++ b/aegisub/src/dialog_selected_choices.h @@ -18,11 +18,8 @@ /// @brief wxMultiChoiceDialog with Select All and Select None /// @ingroup -#include +#include #include -#include -#include -#include /// @class SelectedChoicesDialog /// @brief wxMultiChoiceDialog with Select All and Select None diff --git a/aegisub/src/dialog_selection.cpp b/aegisub/src/dialog_selection.cpp index 46fc778db..6de8242c7 100644 --- a/aegisub/src/dialog_selection.cpp +++ b/aegisub/src/dialog_selection.cpp @@ -38,13 +38,15 @@ #include #include +#include +#include #include #include #include #include #include -#include +#include #include enum { @@ -60,12 +62,10 @@ enum { MODE_REGEXP }; -static std::set process(wxString const& match_text, bool match_case, int mode, bool invert, bool comments, bool dialogue, int field_n, AssFile *ass) { - wxRegEx re; - +static std::set process(std::string const& match_text, bool match_case, int mode, bool invert, bool comments, bool dialogue, int field_n, AssFile *ass) { SearchReplaceSettings settings = { match_text, - wxString(), + std::string(), static_cast(field_n), SearchReplaceSettings::Limit::ALL, match_case, @@ -75,7 +75,7 @@ static std::set process(wxString const& match_text, bool match_cas mode == MODE_EXACT }; - auto predicate = SearchReplaceEngine::GetMatcher(settings, &re); + auto predicate = SearchReplaceEngine::GetMatcher(settings); std::set matches; for (auto diag : ass->Line | agi::of_type()) { @@ -173,7 +173,7 @@ void DialogSelection::Process(wxCommandEvent&) { try { matches = process( - match_text->GetValue(), case_sensitive->IsChecked(), + from_wx(match_text->GetValue()), case_sensitive->IsChecked(), match_mode->GetSelection(), select_unmatching_lines->GetValue(), apply_to_comments->IsChecked(), apply_to_dialogue->IsChecked(), dialogue_field->GetSelection(), con->ass); diff --git a/aegisub/src/dialog_shift_times.cpp b/aegisub/src/dialog_shift_times.cpp index da1f62b8e..6b8737789 100644 --- a/aegisub/src/dialog_shift_times.cpp +++ b/aegisub/src/dialog_shift_times.cpp @@ -23,25 +23,6 @@ #include "dialog_shift_times.h" -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - #include "ass_dialogue.h" #include "ass_file.h" #include "ass_time.h" @@ -54,6 +35,24 @@ #include "timeedit_ctrl.h" #include "video_context.h" +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + static wxString get_history_string(json::Object &obj) { wxString filename = to_wx(obj["filename"]); if (filename.empty()) @@ -99,7 +98,7 @@ static wxString get_history_string(json::Object &obj) { DialogShiftTimes::DialogShiftTimes(agi::Context *context) : wxDialog(context->parent, -1, _("Shift Times")) , context(context) -, history_filename(from_wx(StandardPaths::DecodePath("?user/shift_history.json"))) +, history_filename(StandardPaths::DecodePath("?user/shift_history.json")) , history(new json::Array) , timecodes_loaded_slot(context->videoController->AddTimecodesListener(&DialogShiftTimes::OnTimecodesLoaded, this)) , selected_set_changed_slot(context->selectionController->AddSelectionListener(&DialogShiftTimes::OnSelectedSetChanged, this)) @@ -235,7 +234,7 @@ void DialogShiftTimes::OnSelectedSetChanged() { void DialogShiftTimes::OnClear(wxCommandEvent &) { - wxRemoveFile(to_wx(history_filename)); + agi::fs::Remove(history_filename); history_box->Clear(); history->clear(); } @@ -256,7 +255,7 @@ void DialogShiftTimes::OnHistoryClick(wxCommandEvent &evt) { json::Object& obj = (*history)[entry]; if (obj["is by time"]) { - shift_time->SetTime(AssTime(to_wx(obj["amount"]))); + shift_time->SetTime(AssTime((std::string)obj["amount"])); shift_by_time->SetValue(true); OnByTime(evt); } @@ -279,7 +278,7 @@ void DialogShiftTimes::OnHistoryClick(wxCommandEvent &evt) { void DialogShiftTimes::SaveHistory(json::Array const& shifted_blocks) { json::Object new_entry; - new_entry["filename"] = from_wx(wxFileName(context->ass->filename).GetFullName()); + new_entry["filename"] = context->ass->filename.filename().string(); new_entry["is by time"] = shift_by_time->GetValue(); new_entry["is backward"] = shift_backward->GetValue(); new_entry["amount"] = from_wx(shift_by_time->GetValue() ? shift_time->GetValue() : shift_frames->GetValue()); @@ -292,7 +291,7 @@ void DialogShiftTimes::SaveHistory(json::Array const& shifted_blocks) { try { json::Writer::Write(*history, agi::io::Save(history_filename).Get()); } - catch (agi::FileSystemError const& e) { + catch (agi::fs::FileSystemError const& e) { LOG_E("dialog_shift_times/save_history") << "Cannot save shift times history: " << e.GetChainedMessage(); } } @@ -302,7 +301,7 @@ void DialogShiftTimes::LoadHistory() { history_box->Freeze(); try { - agi::scoped_ptr file(agi::io::Open(history_filename)); + std::unique_ptr file(agi::io::Open(history_filename)); json::UnknownElement root; json::Reader::Read(root, *file); *history = root; @@ -310,7 +309,7 @@ void DialogShiftTimes::LoadHistory() { for (auto& history_entry : *history) history_box->Append(get_history_string(history_entry)); } - catch (agi::FileSystemError const& e) { + catch (agi::fs::FileSystemError const& e) { LOG_D("dialog_shift_times/load_history") << "Cannot load shift times history: " << e.GetChainedMessage(); } catch (...) { diff --git a/aegisub/src/dialog_shift_times.h b/aegisub/src/dialog_shift_times.h index 124b04872..06a4cc3c6 100644 --- a/aegisub/src/dialog_shift_times.h +++ b/aegisub/src/dialog_shift_times.h @@ -19,15 +19,17 @@ /// @ingroup secondary_ui /// -#include - -#include +#include "selection_controller.h" +#include #include #include #include -#include "selection_controller.h" +#include +#include + +#include class AssDialogue; class TimeEdit; @@ -44,7 +46,7 @@ namespace json { class DialogShiftTimes : public wxDialog { agi::Context *context; - std::string history_filename; + agi::fs::path history_filename; agi::scoped_ptr history; agi::vfr::Framerate fps; agi::signal::Connection timecodes_loaded_slot; diff --git a/aegisub/src/dialog_spellchecker.cpp b/aegisub/src/dialog_spellchecker.cpp index f7489d9b0..7d0cc0c93 100644 --- a/aegisub/src/dialog_spellchecker.cpp +++ b/aegisub/src/dialog_spellchecker.cpp @@ -242,7 +242,7 @@ bool DialogSpellChecker::FindNext() { bool DialogSpellChecker::CheckLine(AssDialogue *active_line, int start_pos, int *commit_id) { if (active_line->Comment && skip_comments->GetValue()) return false; - std::string text = from_wx(active_line->Text); + std::string text = active_line->Text; auto tokens = agi::ass::TokenizeDialogueBody(text); agi::ass::SplitWords(text, tokens); @@ -277,7 +277,7 @@ bool DialogSpellChecker::CheckLine(AssDialogue *active_line, int start_pos, int } text.replace(word_start, word_len, auto_rep->second); - active_line->Text = to_wx(text); + active_line->Text = text; *commit_id = context->ass->Commit(_("spell check replace"), AssFile::COMMIT_DIAG_TEXT, *commit_id); word_start += auto_rep->second.size(); } @@ -288,9 +288,9 @@ void DialogSpellChecker::Replace() { AssDialogue *active_line = context->selectionController->GetActiveLine(); // Only replace if the user hasn't changed the selection to something else - if (active_line->Text.get().Mid(word_start, word_len) == orig_word->GetValue()) { - wxString text = active_line->Text; - text.replace(word_start, word_len, replace_word->GetValue()); + if (to_wx(active_line->Text.get().substr(word_start, word_len)) == orig_word->GetValue()) { + std::string text = active_line->Text; + text.replace(word_start, word_len, from_wx(replace_word->GetValue())); active_line->Text = text; context->ass->Commit(_("spell check replace"), AssFile::COMMIT_DIAG_TEXT); context->textSelectionController->SetInsertionPoint(word_start + replace_word->GetValue().size()); diff --git a/aegisub/src/dialog_style_editor.cpp b/aegisub/src/dialog_style_editor.cpp index 5fce0d29f..f0d621af1 100644 --- a/aegisub/src/dialog_style_editor.cpp +++ b/aegisub/src/dialog_style_editor.cpp @@ -86,13 +86,10 @@ class StyleRenamer { found_any = false; do_replace = replace; - wxString wx_old(to_wx(source_name)); - wxString wx_new(to_wx(new_name)); - for (auto diag : c->ass->Line | agi::of_type()) { - if (diag->Style == wx_old) { + if (diag->Style == source_name) { if (replace) - diag->Style = wx_new; + diag->Style = new_name; else found_any = true; } @@ -328,7 +325,7 @@ DialogStyleEditor::DialogStyleEditor(wxWindow *parent, AssStyle *style, agi::Con SubsPreview->SetToolTip(_("Preview of current style")); SubsPreview->SetStyle(*style); - SubsPreview->SetText(PreviewText->GetValue()); + SubsPreview->SetText(from_wx(PreviewText->GetValue())); PreviewText->SetToolTip(_("Text to be used for the preview")); previewButton->SetToolTip(_("Color of preview background")); @@ -524,7 +521,7 @@ void DialogStyleEditor::OnChildFocus(wxChildFocusEvent &event) { } void DialogStyleEditor::OnPreviewTextChange (wxCommandEvent &event) { - SubsPreview->SetText(PreviewText->GetValue()); + SubsPreview->SetText(from_wx(PreviewText->GetValue())); event.Skip(); } diff --git a/aegisub/src/dialog_style_manager.cpp b/aegisub/src/dialog_style_manager.cpp index bce13eed0..a5e729359 100644 --- a/aegisub/src/dialog_style_manager.cpp +++ b/aegisub/src/dialog_style_manager.cpp @@ -35,20 +35,6 @@ #include "dialog_style_manager.h" -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // Keep this last so wxUSE_CHOICEDLG is set. - #include "ass_dialogue.h" #include "ass_file.h" #include "ass_style.h" @@ -65,8 +51,23 @@ #include "subtitle_format.h" #include "utils.h" +#include #include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include // Keep this last so wxUSE_CHOICEDLG is set. + using std::placeholders::_1; namespace { @@ -122,10 +123,11 @@ std::string unique_name(Func name_checker, std::string const& source_name) { template void add_styles(Func1 name_checker, Func2 style_adder) { - wxStringTokenizer st(GetClipboard(), '\n'); - while (st.HasMoreTokens()) { + boost::char_separator sep("\n"); + for (auto tok : boost::tokenizer>(GetClipboard(), sep)) { + boost::trim(tok); try { - AssStyle *s = new AssStyle(st.GetNextToken().Trim(true)); + AssStyle *s = new AssStyle(tok); s->name = unique_name(name_checker, s->name); style_adder(s); } @@ -284,7 +286,7 @@ void DialogStyleManager::LoadCurrentStyles(int commit_type) { AssDialogue *dia = c->selectionController->GetActiveLine(); CurrentList->DeselectAll(); if (dia && commit_type != AssFile::COMMIT_NEW) - CurrentList->SetStringSelection(dia->Style); + CurrentList->SetStringSelection(to_wx(dia->Style)); else CurrentList->SetSelection(0); } @@ -295,7 +297,7 @@ void DialogStyleManager::LoadCurrentStyles(int commit_type) { void DialogStyleManager::OnActiveLineChanged(AssDialogue *new_line) { if (new_line) { CurrentList->DeselectAll(); - CurrentList->SetStringSelection(new_line->Style); + CurrentList->SetStringSelection(to_wx(new_line->Style)); UpdateButtons(); } } @@ -310,8 +312,9 @@ void DialogStyleManager::UpdateStorage() { } void DialogStyleManager::OnChangeCatalog() { - c->ass->SetScriptInfo("Last Style Storage", CatalogList->GetStringSelection()); - Store.Load(CatalogList->GetStringSelection()); + std::string catalog(from_wx(CatalogList->GetStringSelection())); + c->ass->SetScriptInfo("Last Style Storage", catalog); + Store.Load(catalog); UpdateStorage(); } @@ -319,14 +322,8 @@ void DialogStyleManager::LoadCatalog() { CatalogList->Clear(); // Get saved style catalogs - wxDir dir(StandardPaths::DecodePath("?user/catalog/")); - if (dir.IsOpened()) { - wxString curfile; - if (dir.GetFirst(&curfile, "*.sty", wxDIR_FILES)) - CatalogList->Append(wxFileName(curfile).GetName()); - while (dir.GetNext(&curfile)) - CatalogList->Append(wxFileName(curfile).GetName()); - } + for (auto const& file : agi::fs::DirectoryIterator(StandardPaths::DecodePath("?user/catalog/"), "*.sty")) + CatalogList->Append(agi::fs::path(file).stem().wstring()); // Create a default storage if there are none if (CatalogList->IsListEmpty()) { @@ -337,11 +334,11 @@ void DialogStyleManager::LoadCatalog() { } // Set to default if available - wxString pickStyle = c->ass->GetScriptInfo("Last Style Storage"); + std::string pickStyle = c->ass->GetScriptInfo("Last Style Storage"); if (pickStyle.empty()) pickStyle = "Default"; - int opt = CatalogList->FindString(pickStyle, false); + int opt = CatalogList->FindString(to_wx(pickStyle), false); if (opt != wxNOT_FOUND) CatalogList->SetSelection(opt); else @@ -390,7 +387,7 @@ void DialogStyleManager::OnCatalogDelete() { wxString message = wxString::Format(_("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) { - wxRemoveFile(StandardPaths::DecodePath("?user/catalog/" + name + ".sty")); + agi::fs::Remove(StandardPaths::DecodePath("?user/catalog/" + from_wx(name) + ".sty")); CatalogList->Delete(CatalogList->GetSelection()); CatalogList->SetSelection(0); OnChangeCatalog(); @@ -462,7 +459,7 @@ void DialogStyleManager::CopyToClipboard(wxListBox *list, T const& v) { if (i) data += "\r\n"; AssStyle *s = v[selections[i]]; s->UpdateData(); - data += s->GetEntryData(); + data += to_wx(s->GetEntryData()); } SetClipboard(data); @@ -566,14 +563,14 @@ void DialogStyleManager::OnCurrentDelete() { void DialogStyleManager::OnCurrentImport() { // Get file name wxString path = to_wx(OPT_GET("Path/Last/Subtitles")->GetString()); - wxString filename = wxFileSelector(_("Open subtitles file"),path,"","",SubtitleFormat::GetWildcards(0),wxFD_OPEN | wxFD_FILE_MUST_EXIST); + wxString filename = wxFileSelector(_("Open subtitles file"), path, "", "", to_wx(SubtitleFormat::GetWildcards(0)), wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (!filename) return; OPT_SET("Path/Last/Subtitles")->SetString(from_wx(wxFileName(filename).GetPath())); AssFile temp; try { - temp.Load(filename); + temp.Load(from_wx(filename)); } catch (agi::Exception const& err) { wxMessageBox(to_wx(err.GetChainedMessage()), "Error", wxOK | wxICON_ERROR | wxCENTER, this); @@ -758,7 +755,7 @@ void DialogStyleManager::MoveStyles(bool storage, int type) { do_move(styleMap, type, first, last, false); // Replace styles - int curn = 0; + size_t curn = 0; for (auto it = c->ass->Line.begin(); it != c->ass->Line.end(); ++it) { if (!dynamic_cast(&*it)) continue; diff --git a/aegisub/src/dialog_styling_assistant.cpp b/aegisub/src/dialog_styling_assistant.cpp index 1eb03cc97..00a0f8a19 100644 --- a/aegisub/src/dialog_styling_assistant.cpp +++ b/aegisub/src/dialog_styling_assistant.cpp @@ -48,7 +48,7 @@ static void add_hotkey(wxSizer *sizer, wxWindow *parent, const char *command, wxString const& text) { sizer->Add(new wxStaticText(parent, -1, text)); - sizer->Add(new wxStaticText(parent, -1, hotkey::get_hotkey_str_first("Styling Assistant", command))); + sizer->Add(new wxStaticText(parent, -1, to_wx(hotkey::get_hotkey_str_first("Styling Assistant", command)))); } DialogStyling::DialogStyling(agi::Context *context) @@ -158,12 +158,12 @@ void DialogStyling::OnActiveLineChanged(AssDialogue *new_line) { if (!new_line) return; active_line = new_line; - current_line_text->SetValue(active_line->Text); - style_name->SetValue(active_line->Style); + current_line_text->SetValue(to_wx(active_line->Text)); + style_name->SetValue(to_wx(active_line->Style)); style_name->SetSelection(0, active_line->Style.get().size()); style_name->SetFocus(); - style_list->SetStringSelection(active_line->Style); + style_list->SetStringSelection(to_wx(active_line->Style)); if (auto_seek->IsChecked() && IsActive()) c->videoController->JumpToTime(active_line->Start); @@ -172,7 +172,7 @@ void DialogStyling::OnActiveLineChanged(AssDialogue *new_line) { void DialogStyling::Commit(bool next) { if (!c->ass->GetStyle(from_wx(style_name->GetValue()))) return; - active_line->Style = style_name->GetValue(); + active_line->Style = from_wx(style_name->GetValue()); c->ass->Commit(_("styling assistant"), AssFile::COMMIT_DIAG_META); if (next) cmd::call("grid/line/next", c); diff --git a/aegisub/src/dialog_timing_processor.cpp b/aegisub/src/dialog_timing_processor.cpp index 991354066..b08882028 100644 --- a/aegisub/src/dialog_timing_processor.cpp +++ b/aegisub/src/dialog_timing_processor.cpp @@ -291,15 +291,15 @@ void DialogTimingProcessor::OnApply(wxCommandEvent &) { EndModal(0); } -static bool bad_line(std::set *styles, AssDialogue *d) { +static bool bad_line(std::set *styles, AssDialogue *d) { return !d || d->Comment || styles->find(d->Style) == styles->end(); } std::vector DialogTimingProcessor::SortDialogues() { - std::set styles; + std::set styles; for (size_t i = 0; i < StyleList->GetCount(); ++i) { if (StyleList->IsChecked(i)) - styles.insert(StyleList->GetString(i)); + styles.insert(from_wx(StyleList->GetString(i))); } std::vector sorted; diff --git a/aegisub/src/dialog_translation.cpp b/aegisub/src/dialog_translation.cpp index 10ae3e80f..5b732fb55 100644 --- a/aegisub/src/dialog_translation.cpp +++ b/aegisub/src/dialog_translation.cpp @@ -50,7 +50,7 @@ static void add_hotkey(wxSizer *sizer, wxWindow *parent, const char *command, wxString const& text) { sizer->Add(new wxStaticText(parent, -1, text)); - sizer->Add(new wxStaticText(parent, -1, hotkey::get_hotkey_str_first("Translation Assistant", command))); + sizer->Add(new wxStaticText(parent, -1, to_wx(hotkey::get_hotkey_str_first("Translation Assistant", command)))); } // Skip over override blocks, comments, and whitespace between blocks diff --git a/aegisub/src/dialog_version_check.cpp b/aegisub/src/dialog_version_check.cpp index f35f61571..1437190c7 100644 --- a/aegisub/src/dialog_version_check.cpp +++ b/aegisub/src/dialog_version_check.cpp @@ -32,14 +32,26 @@ /// @ingroup configuration_ui /// - #include "config.h" #ifdef WITH_UPDATE_CHECKER #include "dialog_version_check.h" -#include +#ifdef _MSC_VER +#pragma warning(disable : 4250) // 'boost::asio::basic_socket_iostream' : inherits 'std::basic_ostream<_Elem,_Traits>::std::basic_ostream<_Elem,_Traits>::_Add_vtordisp2' via dominance +#endif + +#include "compat.h" +#include "options.h" +#include "string_codec.h" +#include "version.h" + +#include +#include +#include +#include + #include #include #include @@ -47,429 +59,40 @@ #include #include #include -#include #include #include #include #include #include -#include -#include -#include #include +#include +#include +#include +#include +#include #include #include #include #include -#include - -#include "compat.h" -#include "options.h" -#include "string_codec.h" -#include "version.h" - -#include -#include - #ifdef __APPLE__ #include #endif -/* *** Public API is implemented here *** */ - // Allocate global lock mutex declared in header -wxMutex VersionCheckLock; - -class AegisubVersionCheckerThread : public wxThread { - bool interactive; - void DoCheck(); - void PostErrorEvent(const wxString &error_text); - ExitCode Entry(); -public: - AegisubVersionCheckerThread(bool interactive); -}; - -// Public API for version checker -void PerformVersionCheck(bool interactive) -{ - new AegisubVersionCheckerThread(interactive); -} - -/* *** The actual implementation begins here *** */ +std::mutex VersionCheckLock; +namespace { struct AegisubUpdateDescription { - wxString url; - wxString friendly_name; - wxString description; + std::string url; + std::string friendly_name; + std::string description; + + AegisubUpdateDescription(std::string const& url, std::string const& friendly_name, std::string const& description) + : url(url), friendly_name(friendly_name), description(description) { } }; -class AegisubVersionCheckResultEvent : public wxEvent { - wxString main_text; - std::vector updates; - -public: - AegisubVersionCheckResultEvent(wxString message = wxString()); - - - wxEvent *Clone() const - { - return new AegisubVersionCheckResultEvent(*this); - } - - const wxString & GetMainText() const { return main_text; } - - // If there are no updates in the list, either none were found or an error occurred, - // either way it means "failure" if it's empty - const std::vector & GetUpdates() const { return updates; } - void AddUpdate(const wxString &url, const wxString &friendly_name, const wxString &description) - { - updates.push_back(AegisubUpdateDescription()); - AegisubUpdateDescription &desc = updates.back(); - desc.url = url; - desc.friendly_name = friendly_name; - desc.description = description; - } -}; - -wxDEFINE_EVENT(AEGISUB_EVENT_VERSIONCHECK_RESULT, AegisubVersionCheckResultEvent); - -AegisubVersionCheckResultEvent::AegisubVersionCheckResultEvent(wxString message) -: wxEvent(0, AEGISUB_EVENT_VERSIONCHECK_RESULT) -, main_text(message) -{ -} - - -DEFINE_SIMPLE_EXCEPTION_NOINNER(VersionCheckError, agi::Exception, "versioncheck") - -static void register_event_handler(); - -AegisubVersionCheckerThread::AegisubVersionCheckerThread(bool interactive) -: wxThread(wxTHREAD_DETACHED) -, interactive(interactive) -{ - register_event_handler(); - -#ifndef __APPLE__ - if (!wxSocketBase::IsInitialized()) - wxSocketBase::Initialize(); -#endif - - Create(); - Run(); -} - -wxThread::ExitCode AegisubVersionCheckerThread::Entry() -{ - if (!interactive) - { - // Automatic checking enabled? - if (!OPT_GET("App/Auto/Check For Updates")->GetBool()) - return 0; - - // Is it actually time for a check? - time_t next_check = OPT_GET("Version/Next Check")->GetInt(); - if (next_check > wxDateTime::GetTimeNow()) - return 0; - } - - if (VersionCheckLock.TryLock() != wxMUTEX_NO_ERROR) return 0; - - try { - DoCheck(); - } - catch (const agi::Exception &e) { - PostErrorEvent(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."), - e.GetMessage())); - } - catch (...) { - PostErrorEvent(_("An unknown error occurred while checking for updates to Aegisub.")); - } - - VersionCheckLock.Unlock(); - - // While Options isn't perfectly thread safe, this should still be okay. - // Traversing the std::map to find the key-value pair doesn't modify any data as long as - // the key already exists (which it does at this point), and modifying the value only - // touches that specific key-value pair and will never cause a rebalancing of the tree, - // because the tree only depends on the keys. - // Lastly, writing options to disk only happens when Options.Save() is called. - time_t new_next_check_time = wxDateTime::GetTimeNow() + 60*60; // in one hour - OPT_SET("Version/Next Check")->SetInt((int)new_next_check_time); - - return 0; -} - -void AegisubVersionCheckerThread::PostErrorEvent(const wxString &error_text) -{ - if (interactive) - wxTheApp->AddPendingEvent(AegisubVersionCheckResultEvent(error_text)); -} - - -static const char * GetOSShortName() -{ - int osver_maj, osver_min; - wxOperatingSystemId osid = wxGetOsVersion(&osver_maj, &osver_min); - - if (osid & wxOS_WINDOWS_NT) - { - if (osver_maj == 5 && osver_min == 0) - return "win2k"; - else if (osver_maj == 5 && osver_min == 1) - return "winxp"; - else if (osver_maj == 5 && osver_min == 2) - return "win2k3"; // this is also xp64 - else if (osver_maj == 6 && osver_min == 0) - return "win60"; // vista and server 2008 - else if (osver_maj == 6 && osver_min == 1) - return "win61"; // 7 and server 2008r2 - else if (osver_maj == 6 && osver_min == 2) - return "win62"; // 8 - else - return "windows"; // future proofing? I doubt we run on nt4 - } - // CF returns 0x10 for some reason, which wx has recently started - // turning into 10 - else if (osid & wxOS_MAC_OSX_DARWIN && (osver_maj == 0x10 || osver_maj == 10)) - { - // ugliest hack in the world? nah. - static char osxstring[] = "osx00"; - char minor = osver_min >> 4; - char patch = osver_min & 0x0F; - osxstring[3] = minor + ((minor<=9) ? '0' : ('a'-1)); - osxstring[4] = patch + ((patch<=9) ? '0' : ('a'-1)); - return osxstring; - } - else if (osid & wxOS_UNIX_LINUX) - return "linux"; - else if (osid & wxOS_UNIX_FREEBSD) - return "freebsd"; - else if (osid & wxOS_UNIX_OPENBSD) - return "openbsd"; - else if (osid & wxOS_UNIX_NETBSD) - return "netbsd"; - else if (osid & wxOS_UNIX_SOLARIS) - return "solaris"; - else if (osid & wxOS_UNIX_AIX) - return "aix"; - else if (osid & wxOS_UNIX_HPUX) - return "hpux"; - else if (osid & wxOS_UNIX) - return "unix"; - else if (osid & wxOS_OS2) - return "os2"; - else if (osid & wxOS_DOS) - return "dos"; - else - return "unknown"; -} - - -#ifdef WIN32 -typedef BOOL (WINAPI * PGetUserPreferredUILanguages)(DWORD dwFlags, PULONG pulNumLanguages, wchar_t *pwszLanguagesBuffer, PULONG pcchLanguagesBuffer); - -// Try using Win 6+ functions if available -static wxString GetUILanguage() -{ - agi::scoped_holder kernel32(LoadLibraryW(L"kernel32.dll"), FreeLibrary); - if (!kernel32) return ""; - - PGetUserPreferredUILanguages gupuil = (PGetUserPreferredUILanguages)GetProcAddress(kernel32, "GetUserPreferredUILanguages"); - if (!gupuil) return ""; - - ULONG numlang = 0, output_len = 0; - if (gupuil(MUI_LANGUAGE_NAME, &numlang, 0, &output_len) != TRUE || !output_len) - return ""; - - std::vector output(output_len); - if (!gupuil(MUI_LANGUAGE_NAME, &numlang, &output[0], &output_len) || numlang < 1) - return ""; - - // We got at least one language, just treat it as the only, and a null-terminated string - return &output[0]; -} -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()); - } - - return res; -} -#elif __APPLE__ -static wxString GetSystemLanguage() -{ - CFLocaleRef locale = CFLocaleCopyCurrent(); - CFStringRef localeName = (CFStringRef)CFLocaleGetValue(locale, kCFLocaleIdentifier); - - char buf[128] = { 0 }; - CFStringGetCString(localeName, buf, sizeof buf, kCFStringEncodingUTF8); - CFRelease(locale); - - return wxString::FromUTF8(buf); - -} -#else -static wxString GetSystemLanguage() -{ - return wxLocale::GetLanguageInfo(wxLocale::GetSystemLanguage())->CanonicalName; -} -#endif - -static wxString GetAegisubLanguage() -{ - return to_wx(OPT_GET("App/Language")->GetString()); -} - -template -static void split_str(wxString const& str, wxString const& sep, bool empty, OutIter out) -{ - wxStringTokenizer tk(str, sep, empty ? wxTOKEN_DEFAULT : wxTOKEN_RET_EMPTY_ALL); - while (tk.HasMoreTokens()) - { - *out++ = tk.GetNextToken(); - } -} - - -void AegisubVersionCheckerThread::DoCheck() -{ - std::set accept_tags; -#ifdef UPDATE_CHECKER_ACCEPT_TAGS - split_str(wxString(UPDATE_CHECKER_ACCEPT_TAGS, wxConvUTF8), " ", false, - inserter(accept_tags, accept_tags.end())); -#endif - -#ifdef __APPLE__ - wxString cmd = wxString::Format( - "curl --silent --fail 'http://%s/%s?rev=%d&rel=%d&os=%s&lang=%s&aegilang=%s'", - UPDATE_CHECKER_SERVER, - UPDATE_CHECKER_BASE_URL, - GetSVNRevision(), - GetIsOfficialRelease()?1:0, - GetOSShortName(), - GetSystemLanguage(), - GetAegisubLanguage()); - - // wxExecute only works on the main thread so use popen instead - char buf[1024]; - FILE *pipe = popen(cmd.utf8_str(), "r"); - if (!pipe) - throw VersionCheckError("Could not run curl"); - - wxString update_str; - while(fgets(buf, sizeof(buf), pipe)) - update_str += buf; - - int ret = pclose(pipe); - switch (ret) - { - case 0: - break; - case 6: - case 7: - throw VersionCheckError(from_wx(_("Could not connect to updates server."))); - case 22: - throw VersionCheckError(from_wx(_("Could not download from updates server."))); - default: - throw VersionCheckError(from_wx(wxString::Format("curl failed with error code %d", ret))); - } - - agi::scoped_ptr stream(new wxStringInputStream(update_str)); -#else - wxString path = wxString::Format( - "%s?rev=%d&rel=%d&os=%s&lang=%s&aegilang=%s", - UPDATE_CHECKER_BASE_URL, - GetSVNRevision(), - GetIsOfficialRelease()?1:0, - GetOSShortName(), - GetSystemLanguage(), - GetAegisubLanguage()); - - wxHTTP http; - http.SetHeader("User-Agent", wxString("Aegisub ") + GetAegisubLongVersionString()); - http.SetHeader("Connection", "Close"); - http.SetFlags(wxSOCKET_WAITALL | wxSOCKET_BLOCK); - - if (!http.Connect(UPDATE_CHECKER_SERVER)) - throw VersionCheckError(from_wx(_("Could not connect to updates server."))); - - agi::scoped_ptr stream(http.GetInputStream(path)); - if (!stream) // check for null-pointer - throw VersionCheckError(from_wx(_("Could not download from updates server."))); - - if (http.GetResponse() < 200 || http.GetResponse() >= 300) { - throw VersionCheckError(from_wx(wxString::Format(_("HTTP request failed, got HTTP response %d."), http.GetResponse()))); - } -#endif - wxTextInputStream text(*stream); - - AegisubVersionCheckResultEvent result_event; - - while (!stream->Eof() && stream->GetSize() > 0) - { - wxArrayString parsed; - split_str(text.ReadLine(), "|", true, std::back_inserter(parsed)); - if (parsed.size() != 6) continue; - - wxString line_type = parsed[0]; - wxString line_revision = parsed[1]; - wxString line_tags_str = parsed[2]; - wxString line_url = inline_string_decode(parsed[3]); - wxString line_friendlyname = inline_string_decode(parsed[4]); - wxString line_description = inline_string_decode(parsed[5]); - - // stable runners don't want unstable, not interesting, skip - if ((line_type == "branch" || line_type == "dev") && GetIsOfficialRelease()) - continue; - - // check if the tags match - if (line_tags_str.empty() || line_tags_str == "all") - { - // looking good - } - else - { - std::set tags; - split_str(line_tags_str, " ", false, inserter(tags, tags.end())); - if (!includes(accept_tags.begin(), accept_tags.end(), tags.begin(), tags.end())) - continue; - } - - if (line_type == "upgrade" || line_type == "bugfix") - { - // de facto interesting - } - else - { - // maybe interesting, check revision - - long new_revision = 0; - if (!line_revision.ToLong(&new_revision)) continue; - if (new_revision <= GetSVNRevision()) - { - // too old, not interesting, skip - continue; - } - } - - // it's interesting! - result_event.AddUpdate(line_url, line_friendlyname, line_description); - } - - if (result_event.GetUpdates().size() > 0 || interactive) - { - wxTheApp->AddPendingEvent(result_event); - } -} - class VersionCheckerResultDialog : public wxDialog { void OnCloseButton(wxCommandEvent &evt); void OnRemindMeLater(wxCommandEvent &evt); @@ -478,12 +101,12 @@ class VersionCheckerResultDialog : public wxDialog { wxCheckBox *automatic_check_checkbox; public: - VersionCheckerResultDialog(const wxString &main_text, const std::vector &updates); + VersionCheckerResultDialog(wxString const& main_text, const std::vector &updates); bool ShouldPreventAppExit() const { return false; } }; -VersionCheckerResultDialog::VersionCheckerResultDialog(const wxString &main_text, const std::vector &updates) +VersionCheckerResultDialog::VersionCheckerResultDialog(wxString const& main_text, const std::vector &updates) : wxDialog(0, -1, _("Version Checker")) { const int controls_width = 500; @@ -494,21 +117,19 @@ VersionCheckerResultDialog::VersionCheckerResultDialog(const wxString &main_text text->Wrap(controls_width); main_sizer->Add(text, 0, wxBOTTOM|wxEXPAND, 6); - std::vector::const_iterator upd_iterator = updates.begin(); - for (; upd_iterator != updates.end(); ++upd_iterator) - { + for (auto const& update : updates) { main_sizer->Add(new wxStaticLine(this), 0, wxEXPAND|wxALL, 6); - text = new wxStaticText(this, -1, upd_iterator->friendly_name); + text = new wxStaticText(this, -1, to_wx(update.friendly_name)); wxFont boldfont = text->GetFont(); boldfont.SetWeight(wxFONTWEIGHT_BOLD); text->SetFont(boldfont); main_sizer->Add(text, 0, wxEXPAND|wxBOTTOM, 6); - wxTextCtrl *descbox = new wxTextCtrl(this, -1, upd_iterator->description, wxDefaultPosition, wxSize(controls_width,60), wxTE_MULTILINE|wxTE_READONLY); + wxTextCtrl *descbox = new wxTextCtrl(this, -1, to_wx(update.description), wxDefaultPosition, wxSize(controls_width,60), wxTE_MULTILINE|wxTE_READONLY); main_sizer->Add(descbox, 0, wxEXPAND|wxBOTTOM, 6); - main_sizer->Add(new wxHyperlinkCtrl(this, -1, upd_iterator->url, upd_iterator->url), 0, wxALIGN_LEFT|wxBOTTOM, 6); + main_sizer->Add(new wxHyperlinkCtrl(this, -1, to_wx(update.url), to_wx(update.url)), 0, wxALIGN_LEFT|wxBOTTOM, 6); } automatic_check_checkbox = new wxCheckBox(this, -1, _("&Auto Check for Updates")); @@ -544,50 +165,246 @@ VersionCheckerResultDialog::VersionCheckerResultDialog(const wxString &main_text Bind(wxEVT_COMMAND_BUTTON_CLICKED, &VersionCheckerResultDialog::OnRemindMeLater, this, wxID_NO); Bind(wxEVT_CLOSE_WINDOW, &VersionCheckerResultDialog::OnClose, this); } -void VersionCheckerResultDialog::OnRemindMeLater(wxCommandEvent &) -{ + +void VersionCheckerResultDialog::OnRemindMeLater(wxCommandEvent &) { // In one week - time_t new_next_check_time = wxDateTime::Today().GetTicks() + 7*24*60*60; - OPT_SET("Version/Next Check")->SetInt((int)new_next_check_time); + time_t new_next_check_time = time(nullptr) + 7*24*60*60; + OPT_SET("Version/Next Check")->SetInt(new_next_check_time); Close(); } -void VersionCheckerResultDialog::OnClose(wxCloseEvent &) -{ +void VersionCheckerResultDialog::OnClose(wxCloseEvent &) { OPT_SET("App/Auto/Check For Updates")->SetBool(automatic_check_checkbox->GetValue()); Destroy(); } -static void on_update_result(AegisubVersionCheckResultEvent &evt) -{ - wxString text = evt.GetMainText(); - if (!text) - { - if (evt.GetUpdates().size() == 1) - { - text = _("An update to Aegisub was found."); - } - else if (evt.GetUpdates().size() > 1) - { - text = _("Several possible updates to Aegisub were found."); - } - else - { - text = _("There are no updates to Aegisub."); - } - } +DEFINE_SIMPLE_EXCEPTION_NOINNER(VersionCheckError, agi::Exception, "versioncheck") - new VersionCheckerResultDialog(text, evt.GetUpdates()); +void PostErrorEvent(bool interactive, wxString const& error_text) { + if (interactive) { + agi::dispatch::Main().Async([=]{ + new VersionCheckerResultDialog(error_text, std::vector()); + }); + } } -static void register_event_handler() -{ - static bool is_registered = false; - if (is_registered) return; +static const char * GetOSShortName() { + int osver_maj, osver_min; + wxOperatingSystemId osid = wxGetOsVersion(&osver_maj, &osver_min); - wxTheApp->Bind(AEGISUB_EVENT_VERSIONCHECK_RESULT, on_update_result); - is_registered = true; + if (osid & wxOS_WINDOWS_NT) { + if (osver_maj == 5 && osver_min == 0) + return "win2k"; + else if (osver_maj == 5 && osver_min == 1) + return "winxp"; + else if (osver_maj == 5 && osver_min == 2) + return "win2k3"; // this is also xp64 + else if (osver_maj == 6 && osver_min == 0) + return "win60"; // vista and server 2008 + else if (osver_maj == 6 && osver_min == 1) + return "win61"; // 7 and server 2008r2 + else if (osver_maj == 6 && osver_min == 2) + return "win62"; // 8 + else + return "windows"; // future proofing? I doubt we run on nt4 + } + // CF returns 0x10 for some reason, which wx has recently started + // turning into 10 + else if (osid & wxOS_MAC_OSX_DARWIN && (osver_maj == 0x10 || osver_maj == 10)) { + // ugliest hack in the world? nah. + static char osxstring[] = "osx00"; + char minor = osver_min >> 4; + char patch = osver_min & 0x0F; + osxstring[3] = minor + ((minor<=9) ? '0' : ('a'-1)); + osxstring[4] = patch + ((patch<=9) ? '0' : ('a'-1)); + return osxstring; + } + else if (osid & wxOS_UNIX_LINUX) + return "linux"; + else if (osid & wxOS_UNIX_FREEBSD) + return "freebsd"; + else if (osid & wxOS_UNIX_OPENBSD) + return "openbsd"; + else if (osid & wxOS_UNIX_NETBSD) + return "netbsd"; + else if (osid & wxOS_UNIX_SOLARIS) + return "solaris"; + else if (osid & wxOS_UNIX_AIX) + return "aix"; + else if (osid & wxOS_UNIX_HPUX) + return "hpux"; + else if (osid & wxOS_UNIX) + return "unix"; + else if (osid & wxOS_OS2) + return "os2"; + else if (osid & wxOS_DOS) + return "dos"; + else + return "unknown"; +} + +#ifdef WIN32 +typedef BOOL (WINAPI * PGetUserPreferredUILanguages)(DWORD dwFlags, PULONG pulNumLanguages, wchar_t *pwszLanguagesBuffer, PULONG pcchLanguagesBuffer); + +// Try using Win 6+ functions if available +static wxString GetUILanguage() { + agi::scoped_holder kernel32(LoadLibraryW(L"kernel32.dll"), FreeLibrary); + if (!kernel32) return ""; + + PGetUserPreferredUILanguages gupuil = (PGetUserPreferredUILanguages)GetProcAddress(kernel32, "GetUserPreferredUILanguages"); + if (!gupuil) return ""; + + ULONG numlang = 0, output_len = 0; + if (gupuil(MUI_LANGUAGE_NAME, &numlang, 0, &output_len) != TRUE || !output_len) + return ""; + + std::vector output(output_len); + if (!gupuil(MUI_LANGUAGE_NAME, &numlang, &output[0], &output_len) || numlang < 1) + return ""; + + // We got at least one language, just treat it as the only, and a null-terminated string + return &output[0]; +} + +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()); + + return res; +} +#elif __APPLE__ +static wxString GetSystemLanguage() { + CFLocaleRef locale = CFLocaleCopyCurrent(); + CFStringRef localeName = (CFStringRef)CFLocaleGetValue(locale, kCFLocaleIdentifier); + + char buf[128] = { 0 }; + CFStringGetCString(localeName, buf, sizeof buf, kCFStringEncodingUTF8); + CFRelease(locale); + + return wxString::FromUTF8(buf); + +} +#else +static wxString GetSystemLanguage() { + return wxLocale::GetLanguageInfo(wxLocale::GetSystemLanguage())->CanonicalName; +} +#endif + +static wxString GetAegisubLanguage() { + return to_wx(OPT_GET("App/Language")->GetString()); +} + +void DoCheck(bool interactive) { + boost::asio::ip::tcp::iostream stream; + stream.connect(UPDATE_CHECKER_SERVER, "http"); + if (!stream) + throw VersionCheckError(from_wx(_("Could not connect to updates server."))); + + stream << boost::format( + "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" + "Accept: */*\r\n" + "Connection: close\r\n\r\n") + % UPDATE_CHECKER_BASE_URL + % GetSVNRevision() + % (GetIsOfficialRelease() ? 1 : 0) + % GetOSShortName() + % GetSystemLanguage() + % GetAegisubLanguage() + % GetAegisubLongVersionString() + % UPDATE_CHECKER_SERVER + ; + + std::string http_version; + stream >> http_version; + int status_code; + stream >> status_code; + 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))); + + stream.ignore(std::numeric_limits::max(), '\n'); + + // Skip the headers since we don't care about them + for (auto const& header : agi::line_iterator(stream)) + if (header.empty()) break; + + std::vector results; + for (auto const& line : agi::line_iterator(stream)) { + if (line.empty()) continue; + + std::vector parsed; + boost::split(parsed, line, boost::is_any_of("|")); + if (parsed.size() != 6) continue; + + // 0 and 2 being things that never got used + std::string revision = parsed[1]; + std::string url = inline_string_decode(parsed[3]); + std::string friendlyname = inline_string_decode(parsed[4]); + std::string description = inline_string_decode(parsed[5]); + + if (atoi(revision.c_str()) <= GetSVNRevision()) + continue; + + results.emplace_back(url, friendlyname, description); + } + + if (!results.empty() || interactive) { + agi::dispatch::Main().Async([=]{ + wxString text; + if (results.size() == 1) + text = _("An update to Aegisub was found."); + else if (results.size() > 1) + text = _("Several possible updates to Aegisub were found."); + else + text = _("There are no updates to Aegisub."); + + new VersionCheckerResultDialog(text, results); + }); + } +} + +} + +void PerformVersionCheck(bool interactive) { + agi::dispatch::Background().Async([=]{ + if (!interactive) { + // Automatic checking enabled? + if (!OPT_GET("App/Auto/Check For Updates")->GetBool()) + return; + + // Is it actually time for a check? + time_t next_check = OPT_GET("Version/Next Check")->GetInt(); + if (next_check > time(nullptr)) + return; + } + + if (!VersionCheckLock.try_lock()) return; + + try { + 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."), + e.GetMessage())); + } + catch (...) { + PostErrorEvent(interactive, _("An unknown error occurred while checking for updates to Aegisub.")); + } + + VersionCheckLock.unlock(); + + agi::dispatch::Main().Async([]{ + time_t new_next_check_time = time(nullptr) + 60*60; // in one hour + OPT_SET("Version/Next Check")->SetInt(new_next_check_time); + }); + }); } #endif diff --git a/aegisub/src/dialog_version_check.h b/aegisub/src/dialog_version_check.h index 6e3c5be7e..d556d753a 100644 --- a/aegisub/src/dialog_version_check.h +++ b/aegisub/src/dialog_version_check.h @@ -32,7 +32,7 @@ /// @ingroup configuration_ui /// -class wxMutex; +#include /// @brief Check whether a newer version is available and report to the user if there is /// @param interactive If true, always check and report all results, both success and failure. @@ -40,10 +40,9 @@ class wxMutex; /// new version actually exists. void PerformVersionCheck(bool interactive); - /// @brief Mutex that is taken while version checking is being performed. /// /// A new version check can't be performed while this mutex is locked, checking whether /// it is locked is a way to disable UI to invoke a version check while one is being /// performed. -extern wxMutex VersionCheckLock; +extern std::mutex VersionCheckLock; diff --git a/aegisub/src/dialog_video_details.cpp b/aegisub/src/dialog_video_details.cpp index 0f2043dfe..7d16eec9a 100644 --- a/aegisub/src/dialog_video_details.cpp +++ b/aegisub/src/dialog_video_details.cpp @@ -34,17 +34,18 @@ #include "config.h" -#include -#include -#include - #include "dialog_video_details.h" +#include "compat.h" #include "include/aegisub/context.h" #include "utils.h" #include "video_context.h" #include "video_provider_manager.h" +#include +#include +#include + static void make_field(wxWindow *parent, wxSizer *sizer, wxString const& name, wxString const& value) { sizer->Add(new wxStaticText(parent, -1, name), 0, wxALIGN_CENTRE_VERTICAL); sizer->Add(new wxTextCtrl(parent, -1, value, wxDefaultPosition, wxSize(300,-1), wxTE_READONLY), 0, wxALIGN_CENTRE_VERTICAL | wxEXPAND); @@ -79,11 +80,11 @@ DialogVideoDetails::DialogVideoDetails(agi::Context *c) double fps = c->videoController->FPS().FPS(); wxFlexGridSizer *fg = new wxFlexGridSizer(2, 5, 10); - make_field(this, fg, _("File name:"), c->videoController->GetVideoName()); + make_field(this, fg, _("File name:"), c->videoController->GetVideoName().wstring()); make_field(this, fg, _("FPS:"), wxString::Format("%.3f", fps)); make_field(this, fg, _("Resolution:"), wxString::Format("%dx%d (%s)", width, height, pretty_ar(width, height))); make_field(this, fg, _("Length:"), wxString::Format(_("%d frames (%s)"), framecount, pretty_time_stamp(framecount, fps))); - make_field(this, fg, _("Decoder:"), c->videoController->GetProvider()->GetDecoderName()); + make_field(this, fg, _("Decoder:"), to_wx(c->videoController->GetProvider()->GetDecoderName())); wxStaticBoxSizer *video_sizer = new wxStaticBoxSizer(wxVERTICAL,this,_("Video")); video_sizer->Add(fg); diff --git a/aegisub/src/export_fixstyle.cpp b/aegisub/src/export_fixstyle.cpp index f43a5003e..de613258f 100644 --- a/aegisub/src/export_fixstyle.cpp +++ b/aegisub/src/export_fixstyle.cpp @@ -36,18 +36,19 @@ #include "export_fixstyle.h" -#include -#include -#include - #include "ass_file.h" #include "ass_dialogue.h" #include "compat.h" #include +#include +#include +#include +#include + AssFixStylesFilter::AssFixStylesFilter() -: AssExportFilter(_("Fix Styles"), _("Fixes styles by replacing any style that isn't available on file with Default."), -5000) +: AssExportFilter(from_wx(_("Fix Styles")), from_wx(_("Fixes styles by replacing any style that isn't available on file with Default.")), -5000) { } @@ -57,7 +58,7 @@ void AssFixStylesFilter::ProcessSubs(AssFile *subs, wxWindow *) { sort(begin(styles), end(styles)); for (auto diag : subs->Line | agi::of_type()) { - if (!binary_search(begin(styles), end(styles), from_wx(diag->Style.get().Lower()))) + if (!binary_search(begin(styles), end(styles), boost::to_lower_copy(diag->Style.get()))) diag->Style = "Default"; } } diff --git a/aegisub/src/export_framerate.cpp b/aegisub/src/export_framerate.cpp index f734ec2e0..b64267e44 100644 --- a/aegisub/src/export_framerate.cpp +++ b/aegisub/src/export_framerate.cpp @@ -34,6 +34,17 @@ #include "config.h" +#include "export_framerate.h" + +#include "ass_dialogue.h" +#include "ass_file.h" +#include "compat.h" +#include "include/aegisub/context.h" +#include "utils.h" +#include "video_context.h" + +#include + #include #include @@ -44,17 +55,10 @@ #include #include -#include "ass_dialogue.h" -#include "ass_file.h" -#include "export_framerate.h" -#include "include/aegisub/context.h" -#include "utils.h" -#include "video_context.h" - -#include - AssTransformFramerateFilter::AssTransformFramerateFilter() -: AssExportFilter(_("Transform Framerate"), _("Transform subtitle times, including those in override tags, from an input framerate to an output framerate.\n\nThis is useful for converting regular time subtitles to VFRaC time subtitles for hardsubbing.\nIt can also be used to convert subtitles to a different speed video, such as NTSC to PAL speedup."), 1000) +: AssExportFilter(from_wx(_("Transform Framerate")), + from_wx(_("Transform subtitle times, including those in override tags, from an input framerate to an output framerate.\n\nThis is useful for converting regular time subtitles to VFRaC time subtitles for hardsubbing.\nIt can also be used to convert subtitles to a different speed video, such as NTSC to PAL speedup.")), + 1000) , c(0) , line(0) , newStart(0) @@ -81,7 +85,9 @@ wxWindow *AssTransformFramerateFilter::GetConfigDialogWindow(wxWindow *parent, a wxButton *FromVideo = new wxButton(base,-1,_("From &video")); if (Input->IsLoaded()) { initialInput = wxString::Format("%2.3f",Input->FPS()); - FromVideo->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &AssTransformFramerateFilter::OnFpsFromVideo, this); + FromVideo->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [=](wxCommandEvent&) { + InputFramerate->SetValue(wxString::Format("%g", c->videoController->FPS().FPS())); + }); } else { initialInput = "23.976"; @@ -133,10 +139,6 @@ wxWindow *AssTransformFramerateFilter::GetConfigDialogWindow(wxWindow *parent, a return base; } -void AssTransformFramerateFilter::OnFpsFromVideo(wxCommandEvent &) { - InputFramerate->SetValue(wxString::Format("%g", c->videoController->FPS().FPS())); -} - void AssTransformFramerateFilter::LoadSettings(bool is_default, agi::Context *c) { this->c = c; diff --git a/aegisub/src/export_framerate.h b/aegisub/src/export_framerate.h index 380079c6a..c5717c7fc 100644 --- a/aegisub/src/export_framerate.h +++ b/aegisub/src/export_framerate.h @@ -33,6 +33,7 @@ /// #include "ass_export_filter.h" + #include class AssDialogue; @@ -83,9 +84,6 @@ class AssTransformFramerateFilter : public AssExportFilter { /// 2. The relative distance between the beginning of the frame which time /// is in and the beginning of the next frame int ConvertTime(int time); - - /// From Video click handler - void OnFpsFromVideo(wxCommandEvent &); public: /// Constructor AssTransformFramerateFilter(); diff --git a/aegisub/src/factory_manager.h b/aegisub/src/factory_manager.h index 3b17771b4..710a314f3 100644 --- a/aegisub/src/factory_manager.h +++ b/aegisub/src/factory_manager.h @@ -22,12 +22,10 @@ #pragma once #include -#include #include +#include #include -#include - template class FactoryBase { protected: diff --git a/aegisub/src/ffmpegsource_common.cpp b/aegisub/src/ffmpegsource_common.cpp index 106bb157a..8893aaaed 100644 --- a/aegisub/src/ffmpegsource_common.cpp +++ b/aegisub/src/ffmpegsource_common.cpp @@ -45,15 +45,20 @@ #include "standard_paths.h" #include "utils.h" +#include #include +#include #include +#include #include -#include +#include #include // Keep this last so wxUSE_CHOICEDLG is set. -#ifdef WIN32 +#ifdef _WIN32 +#include + static void deinit_com(bool) { CoUninitialize(); } @@ -64,7 +69,7 @@ static void deinit_com(bool) { } FFmpegSourceProvider::FFmpegSourceProvider() : COMInited(false, deinit_com) { -#ifdef WIN32 +#ifdef _WIN32 HRESULT res = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); if (SUCCEEDED(res)) COMInited = true; @@ -76,18 +81,6 @@ FFmpegSourceProvider::FFmpegSourceProvider() FFMS_Init(0, 1); } -/// @brief Callback function that updates the indexing progress dialog -/// @param Current The current file positition in bytes -/// @param Total The total file size in bytes -/// @param Private A pointer to the progress sink to update -/// @return Returns non-0 if indexing is cancelled, 0 otherwise. -/// -static int FFMS_CC UpdateIndexingProgress(int64_t Current, int64_t Total, void *Private) { - agi::ProgressSink *ps = static_cast(Private); - ps->SetProgress(Current, Total); - return ps->IsCancelled(); -} - /// @brief Does indexing of a source file /// @param Indexer A pointer to the indexer object representing the file to be indexed /// @param CacheName The filename of the output index file @@ -95,14 +88,14 @@ static int FFMS_CC UpdateIndexingProgress(int64_t Current, int64_t Total, void * /// @param IgnoreDecodeErrors True if audio decoding errors will be tolerated, false otherwise /// @return Returns the index object on success, nullptr otherwise /// -FFMS_Index *FFmpegSourceProvider::DoIndexing(FFMS_Indexer *Indexer, const wxString &CacheName, int Trackmask, FFMS_IndexErrorHandling IndexEH) { +FFMS_Index *FFmpegSourceProvider::DoIndexing(FFMS_Indexer *Indexer, agi::fs::path const& CacheName, int Trackmask, FFMS_IndexErrorHandling IndexEH) { char FFMSErrMsg[1024]; FFMS_ErrorInfo ErrInfo; ErrInfo.Buffer = FFMSErrMsg; ErrInfo.BufferSize = sizeof(FFMSErrMsg); ErrInfo.ErrorType = FFMS_ERROR_SUCCESS; ErrInfo.SubType = FFMS_ERROR_SUCCESS; - wxString MsgString; + std::string MsgString; // set up progress dialog callback DialogProgress Progress(wxGetApp().frame, _("Indexing"), _("Reading timecodes and frame/sample data")); @@ -110,22 +103,23 @@ FFMS_Index *FFmpegSourceProvider::DoIndexing(FFMS_Indexer *Indexer, const wxStri // index all audio tracks FFMS_Index *Index; Progress.Run([&](agi::ProgressSink *ps) { - Index = FFMS_DoIndexing(Indexer, Trackmask, FFMS_TRACKMASK_NONE, nullptr, nullptr, IndexEH, UpdateIndexingProgress, ps, &ErrInfo); + Index = FFMS_DoIndexing(Indexer, Trackmask, FFMS_TRACKMASK_NONE, nullptr, nullptr, IndexEH, + static_cast([](int64_t Current, int64_t Total, void *Private) -> int { + agi::ProgressSink *ps = static_cast(Private); + ps->SetProgress(Current, Total); + return ps->IsCancelled(); + }), + ps, &ErrInfo); }); if (Index == nullptr) { - MsgString.Append("Failed to index: ").Append(wxString(ErrInfo.Buffer, wxConvUTF8)); + MsgString += "Failed to index: "; + MsgString += ErrInfo.Buffer; throw MsgString; } // write index to disk for later use - // ignore write errors for now - FFMS_WriteIndex(CacheName.utf8_str(), Index, &ErrInfo); - /*if (FFMS_WriteIndex(CacheName.char_str(), Index, FFMSErrMsg, MsgSize)) { - wxString temp(FFMSErrMsg, wxConvUTF8); - MsgString << "Failed to write index: " << temp; - throw MsgString; - } */ + FFMS_WriteIndex(CacheName.string().c_str(), Index, &ErrInfo); return Index; } @@ -134,14 +128,14 @@ FFMS_Index *FFmpegSourceProvider::DoIndexing(FFMS_Indexer *Indexer, const wxStri /// @param Indexer The indexer object representing the source file /// @param Type The track type to look for /// @return Returns a std::map with the track numbers as keys and the codec names as values. -std::map FFmpegSourceProvider::GetTracksOfType(FFMS_Indexer *Indexer, FFMS_TrackType Type) { - std::map TrackList; +std::map FFmpegSourceProvider::GetTracksOfType(FFMS_Indexer *Indexer, FFMS_TrackType Type) { + std::map TrackList; int NumTracks = FFMS_GetNumTracksI(Indexer); for (int i=0; i(i, CodecName)); + std::string CodecName(FFMS_GetCodecNameI(Indexer, i)); + TrackList.insert(std::pair(i, CodecName)); } } return TrackList; @@ -151,12 +145,12 @@ std::map FFmpegSourceProvider::GetTracksOfType(FFMS_Indexer *Index /// @param TrackList A std::map with the track numbers as keys and codec names as values /// @param Type The track type to ask about /// @return Returns the track number chosen (an integer >= 0) on success, or a negative integer if the user cancelled. -int FFmpegSourceProvider::AskForTrackSelection(const std::map &TrackList, FFMS_TrackType Type) { +int FFmpegSourceProvider::AskForTrackSelection(const std::map &TrackList, FFMS_TrackType Type) { std::vector TrackNumbers; wxArrayString Choices; for (auto const& track : TrackList) { - Choices.Add(wxString::Format(_("Track %02d: %s"), track.first, track.second)); + Choices.Add(wxString::Format(_("Track %02d: %s"), track.first, to_wx(track.second))); TrackNumbers.push_back(track.first); } @@ -173,37 +167,36 @@ int FFmpegSourceProvider::AskForTrackSelection(const std::map &Tra /// @brief Set ffms2 log level according to setting in config.dat void FFmpegSourceProvider::SetLogLevel() { - wxString LogLevel = to_wx(OPT_GET("Provider/FFmpegSource/Log Level")->GetString()); + std::string LogLevel = OPT_GET("Provider/FFmpegSource/Log Level")->GetString(); - if (!LogLevel.CmpNoCase("panic")) + if (boost::iequals(LogLevel, "panic")) FFMS_SetLogLevel(FFMS_LOG_PANIC); - else if (!LogLevel.CmpNoCase("fatal")) + else if (boost::iequals(LogLevel, "fatal")) FFMS_SetLogLevel(FFMS_LOG_FATAL); - else if (!LogLevel.CmpNoCase("error")) + else if (boost::iequals(LogLevel, "error")) FFMS_SetLogLevel(FFMS_LOG_ERROR); - else if (!LogLevel.CmpNoCase("warning")) + else if (boost::iequals(LogLevel, "warning")) FFMS_SetLogLevel(FFMS_LOG_WARNING); - else if (!LogLevel.CmpNoCase("info")) + else if (boost::iequals(LogLevel, "info")) FFMS_SetLogLevel(FFMS_LOG_INFO); - else if (!LogLevel.CmpNoCase("verbose")) + else if (boost::iequals(LogLevel, "verbose")) FFMS_SetLogLevel(FFMS_LOG_VERBOSE); - else if (!LogLevel.CmpNoCase("debug")) + else if (boost::iequals(LogLevel, "debug")) FFMS_SetLogLevel(FFMS_LOG_DEBUG); else FFMS_SetLogLevel(FFMS_LOG_QUIET); } - FFMS_IndexErrorHandling FFmpegSourceProvider::GetErrorHandlingMode() { - wxString Mode = to_wx(OPT_GET("Provider/Audio/FFmpegSource/Decode Error Handling")->GetString()); + std::string Mode = OPT_GET("Provider/Audio/FFmpegSource/Decode Error Handling")->GetString(); - if (!Mode.CmpNoCase("ignore")) + if (boost::iequals(Mode, "ignore")) return FFMS_IEH_IGNORE; - else if (!Mode.CmpNoCase("clear")) + else if (boost::iequals(Mode, "clear")) return FFMS_IEH_CLEAR_TRACK; - else if (!Mode.CmpNoCase("stop")) + else if (boost::iequals(Mode, "stop")) return FFMS_IEH_STOP_TRACK; - else if (!Mode.CmpNoCase("abort")) + else if (boost::iequals(Mode, "abort")) return FFMS_IEH_ABORT; else return FFMS_IEH_STOP_TRACK; // questionable default? @@ -212,34 +205,21 @@ FFMS_IndexErrorHandling FFmpegSourceProvider::GetErrorHandlingMode() { /// @brief Generates an unique name for the ffms2 index file and prepares the cache folder if it doesn't exist /// @param filename The name of the source file /// @return Returns the generated filename. -wxString FFmpegSourceProvider::GetCacheFilename(const wxString& _filename) { +agi::fs::path FFmpegSourceProvider::GetCacheFilename(agi::fs::path const& filename) { // Get the size of the file to be hashed - wxFileOffset len = 0; - { - wxFile file(_filename,wxFile::read); - if (file.IsOpened()) len = file.Length(); - } - - wxFileName filename(_filename); + uintmax_t len = agi::fs::Size(filename); // Get the hash of the filename - wxString toHash = filename.GetFullName(); boost::crc_32_type hash; - hash.process_bytes(toHash.wc_str(), toHash.size() * sizeof(wchar_t)); + hash.process_bytes(filename.string().c_str(), filename.string().size()); // Generate the filename - wxString result = wxString::Format("?local/ffms2cache/%u_%" PRId64 "_%" PRId64 ".ffindex", hash.checksum(), len, (int64_t)filename.GetModificationTime().GetTicks()); - result = StandardPaths::DecodePath(result); + auto result = StandardPaths::DecodePath("?local/ffms2cache/" + std::to_string(hash.checksum()) + "_" + std::to_string(len) + "_" + std::to_string(agi::fs::ModifiedTime(filename)) + ".ffindex"); // Ensure that folder exists - wxFileName fn(result); - wxString dir = fn.GetPath(); - if (!wxFileName::DirExists(dir)) { - wxFileName::Mkdir(dir); - } + agi::fs::CreateDirectory(result.parent_path()); - wxFileName dirfn(dir); - return dirfn.GetShortPath() + "/" + fn.GetFullName(); + return result; } /// @brief Starts the cache cleaner thread diff --git a/aegisub/src/ffmpegsource_common.h b/aegisub/src/ffmpegsource_common.h index ec037e6fa..dbb05314b 100644 --- a/aegisub/src/ffmpegsource_common.h +++ b/aegisub/src/ffmpegsource_common.h @@ -33,14 +33,11 @@ /// #ifdef WITH_FFMS2 - #include -#include -#include - #include +#include #include /// Index all tracks @@ -72,10 +69,10 @@ public: void CleanCache(); - FFMS_Index *DoIndexing(FFMS_Indexer *Indexer, const wxString& Cachename, int Trackmask, FFMS_IndexErrorHandling IndexEH); - std::map GetTracksOfType(FFMS_Indexer *Indexer, FFMS_TrackType Type); - int AskForTrackSelection(const std::map& TrackList, FFMS_TrackType Type); - wxString GetCacheFilename(const wxString& filename); + FFMS_Index *DoIndexing(FFMS_Indexer *Indexer, agi::fs::path const& Cachename, int Trackmask, FFMS_IndexErrorHandling IndexEH); + std::map GetTracksOfType(FFMS_Indexer *Indexer, FFMS_TrackType Type); + int AskForTrackSelection(const std::map& TrackList, FFMS_TrackType Type); + agi::fs::path GetCacheFilename(agi::fs::path const& filename); void SetLogLevel(); FFMS_IndexErrorHandling GetErrorHandlingMode(); diff --git a/aegisub/src/font_file_lister.cpp b/aegisub/src/font_file_lister.cpp index 4e5e22377..aacedc3bc 100644 --- a/aegisub/src/font_file_lister.cpp +++ b/aegisub/src/font_file_lister.cpp @@ -32,6 +32,7 @@ #include #include +#include #include @@ -49,7 +50,7 @@ void FontCollector::ProcessDialogueLine(const AssDialogue *line, int index) { if (line->Comment) return; boost::ptr_vector blocks(line->ParseTags()); - StyleInfo style = styles[from_wx(line->Style)]; + StyleInfo style = styles[line->Style]; StyleInfo initial = style; bool overriden = false; @@ -60,7 +61,7 @@ void FontCollector::ProcessDialogueLine(const AssDialogue *line, int index) { std::string const& name = tag.Name; if (name == "\\r") { - style = styles[tag.Params[0].Get(from_wx(line->Style))]; + style = styles[tag.Params[0].Get(line->Style.get())]; overriden = false; } else if (name == "\\b") { @@ -117,7 +118,7 @@ void FontCollector::ProcessChunk(std::pair const& style) { else { for (size_t i = 0; i < res.paths.size(); ++i) { if (results.insert(res.paths[i]).second) - status_callback(wxString::Format(_("Found '%s' at '%s'\n"), style.first.facename, res.paths[i]), 0); + status_callback(wxString::Format(_("Found '%s' at '%s'\n"), style.first.facename, res.paths[i].make_preferred().wstring()), 0); } if (res.missing.size()) { @@ -147,7 +148,7 @@ void FontCollector::PrintUsage(UsageData const& data) { status_callback("\n", 2); } -std::vector FontCollector::GetFontPaths(const AssFile *file) { +std::vector FontCollector::GetFontPaths(const AssFile *file) { missing = 0; missing_glyphs = 0; @@ -169,7 +170,7 @@ std::vector FontCollector::GetFontPaths(const AssFile *file) { for_each(used_styles.begin(), used_styles.end(), bind(&FontCollector::ProcessChunk, this, _1)); status_callback(_("Done\n\n"), 0); - std::vector paths; + std::vector paths; paths.reserve(results.size()); paths.insert(paths.end(), results.begin(), results.end()); @@ -184,15 +185,5 @@ std::vector FontCollector::GetFontPaths(const AssFile *file) { } bool FontCollector::StyleInfo::operator<(StyleInfo const& rgt) const { -#define CMP(field) \ - if (field < rgt.field) return true; \ - if (field > rgt.field) return false - - CMP(facename); - CMP(bold); - CMP(italic); - -#undef CMP - - return false; + return std::tie(facename, bold, italic) < std::tie(rgt.facename, rgt.bold, rgt.italic); } diff --git a/aegisub/src/font_file_lister.h b/aegisub/src/font_file_lister.h index 8156a9699..87efd4a5b 100644 --- a/aegisub/src/font_file_lister.h +++ b/aegisub/src/font_file_lister.h @@ -21,10 +21,13 @@ #pragma once -#include -#include +#include + +#include #include +#include #include +#include #include #include @@ -42,7 +45,7 @@ public: /// Characters which could not be found in any font files wxString missing; /// Paths to the file(s) containing the requested font - std::vector paths; + std::vector paths; }; /// @brief Get the path to the font with the given styles @@ -82,7 +85,7 @@ class FontCollector { /// Style name -> ASS style definition std::map styles; /// Paths to found required font files - std::set results; + std::set results; /// Number of fonts which could not be found int missing; /// Number of fonts which were found, but did not contain all used glyphs @@ -107,5 +110,5 @@ public: /// @param file Lines in the subtitle file to check /// @param status Callback function for messages /// @return List of paths to fonts - std::vector GetFontPaths(const AssFile *file); + std::vector GetFontPaths(const AssFile *file); }; diff --git a/aegisub/src/font_file_lister_fontconfig.cpp b/aegisub/src/font_file_lister_fontconfig.cpp index 4755c5480..ca4cd865c 100644 --- a/aegisub/src/font_file_lister_fontconfig.cpp +++ b/aegisub/src/font_file_lister_fontconfig.cpp @@ -26,8 +26,6 @@ #include -#include "compat.h" - #include #include @@ -36,6 +34,7 @@ #endif #include +#include #include @@ -139,7 +138,7 @@ FontFileLister::CollectionResult FontConfigFontFileLister::GetFontPaths(std::str } } - ret.paths.push_back((const char *)file); + ret.paths.emplace_back((const char *)file); return ret; } #endif diff --git a/aegisub/src/frame_main.cpp b/aegisub/src/frame_main.cpp index d5ee8fc7d..cfaf14c20 100644 --- a/aegisub/src/frame_main.cpp +++ b/aegisub/src/frame_main.cpp @@ -35,16 +35,10 @@ #include "frame_main.h" -#include -#include -#include -#include -#include -#include -#include -#include - +#include #include +#include +#include #include "include/aegisub/context.h" #include "include/aegisub/menu.h" @@ -66,7 +60,6 @@ #include "main.h" #include "options.h" #include "search_replace_engine.h" -#include "standard_paths.h" #include "subs_edit_box.h" #include "subs_edit_ctrl.h" #include "subs_grid.h" @@ -79,10 +72,18 @@ #include "video_provider_manager.h" #include "video_slider.h" +#include + +#include +#include +#include +#include +#include +#include + enum { ID_APP_TIMER_AUTOSAVE = 12001, - ID_APP_TIMER_STATUSCLEAR = 12002, - ID_SASH_MAIN_AUDIO = 14001 + ID_APP_TIMER_STATUSCLEAR = 12002 }; #ifdef WITH_STARTUPLOG @@ -95,7 +96,7 @@ static void autosave_timer_changed(wxTimer *timer); wxDEFINE_EVENT(FILE_LIST_DROPPED, wxThreadEvent); -static void get_files_to_load(wxArrayString const& list, wxString &subs, wxString &audio, wxString &video) { +static void get_files_to_load(wxArrayString const& list, std::string &subs, std::string &audio, std::string &video) { // Keep these lists sorted // Video formats @@ -155,11 +156,11 @@ static void get_files_to_load(wxArrayString const& list, wxString &subs, wxStrin wxString ext = file.GetExt().Lower(); if (subs.empty() && std::binary_search(subsList, subsList + countof(subsList), ext)) - subs = file.GetFullPath(); + subs = from_wx(file.GetFullPath()); if (video.empty() && std::binary_search(videoList, videoList + countof(videoList), ext)) - video = file.GetFullPath(); + video = from_wx(file.GetFullPath()); if (audio.empty() && std::binary_search(audioList, audioList + countof(audioList), ext)) - audio = file.GetFullPath(); + audio = from_wx(file.GetFullPath()); } } @@ -169,10 +170,10 @@ class AegisubFileDropTarget : public wxFileDropTarget { public: AegisubFileDropTarget(FrameMain *parent) : parent(parent) { } bool OnDropFiles(wxCoord, wxCoord, const wxArrayString& filenames) { - wxString subs, audio, video; + std::string subs, audio, video; get_files_to_load(filenames, subs, audio, video); - if (!subs && !audio && !video) + if (subs.empty() && audio.empty() && video.empty()) return false; wxThreadEvent *evt = new wxThreadEvent(FILE_LIST_DROPPED); @@ -407,7 +408,7 @@ void FrameMain::InitContents() { StartupLog("Leaving InitContents"); } -void FrameMain::LoadSubtitles(wxString const& filename, wxString const& charset) { +void FrameMain::LoadSubtitles(agi::fs::path const& filename, std::string const& charset) { if (context->ass->loaded) { if (TryToCloseSubs() == wxCANCEL) return; } @@ -416,8 +417,8 @@ void FrameMain::LoadSubtitles(wxString const& filename, wxString const& charset) // Make sure that file isn't actually a timecode file try { TextFileReader testSubs(filename, charset); - wxString cur = testSubs.ReadLineFromFile(); - if (cur.Left(10) == "# timecode") { + std::string cur = testSubs.ReadLineFromFile(); + if (boost::starts_with(cur, "# timecode")) { context->videoController->LoadTimecodes(filename); return; } @@ -429,29 +430,27 @@ void FrameMain::LoadSubtitles(wxString const& filename, wxString const& charset) context->ass->Load(filename, charset); - wxFileName file(filename); - StandardPaths::SetPathValue("?script", file.GetPath()); - config::mru->Add("Subtitle", from_wx(filename)); - OPT_SET("Path/Last/Subtitles")->SetString(from_wx(file.GetPath())); + StandardPaths::SetPathValue("?script", filename); + config::mru->Add("Subtitle", filename); + OPT_SET("Path/Last/Subtitles")->SetString(filename.parent_path().string()); // Save backup of file if (context->ass->CanSave() && OPT_GET("App/Auto/Backup")->GetBool()) { - if (file.FileExists()) { - wxString path = to_wx(OPT_GET("Path/Auto/Backup")->GetString()); - if (path.empty()) path = file.GetPath(); - wxFileName dstpath(StandardPaths::DecodePath(path + "/")); - if (!dstpath.DirExists()) - wxMkdir(dstpath.GetPath()); - - dstpath.SetFullName(file.GetName() + ".ORIGINAL." + file.GetExt()); - - wxCopyFile(file.GetFullPath(), dstpath.GetFullPath(), true); + if (agi::fs::FileExists(filename)) { + auto path_str = OPT_GET("Path/Auto/Backup")->GetString(); + agi::fs::path path; + if (path_str.empty()) + path = filename.parent_path(); + else + path = StandardPaths::DecodePath(path_str); + agi::fs::CreateDirectory(path); + agi::fs::Copy(filename, path/(filename.stem().string() + ".ORIGINAL" + filename.extension().string())); } } } - catch (agi::FileNotFoundError const&) { - wxMessageBox(filename + " not found.", "Error", wxOK | wxICON_ERROR | wxCENTER, this); - config::mru->Remove("Subtitle", from_wx(filename)); + catch (agi::fs::FileNotFound const&) { + wxMessageBox(filename.wstring() + " not found.", "Error", wxOK | wxICON_ERROR | wxCENTER, this); + config::mru->Remove("Subtitle", filename); return; } catch (agi::Exception const& err) { @@ -561,7 +560,7 @@ void FrameMain::OnVideoOpen() { catch (agi::UserCancelException const&) { } // Opening a video with no audio data isn't an error, so just log // and move on - catch (agi::FileNotAccessibleError const&) { + catch (agi::fs::FileNotFound const&) { LOG_D("video/open/audio") << "File " << context->videoController->GetVideoName() << " found by video provider but not audio provider"; } catch (agi::AudioDataNotFoundError const& e) { @@ -591,7 +590,7 @@ void FrameMain::OnFilesDropped(wxThreadEvent &evt) { } bool FrameMain::LoadList(wxArrayString list) { - wxString audio, video, subs; + std::string audio, video, subs; get_files_to_load(list, subs, audio, video); blockVideoLoad = !video.empty(); @@ -660,19 +659,13 @@ void FrameMain::OnCloseWindow (wxCloseEvent &event) { } void FrameMain::OnAutoSave(wxTimerEvent &) try { - wxString fn = context->ass->AutoSave(); + auto fn = context->ass->AutoSave(); if (!fn.empty()) - StatusTimeout(wxString::Format(_("File backup saved as \"%s\"."), fn)); + StatusTimeout(wxString::Format(_("File backup saved as \"%s\"."), fn.wstring())); } catch (const agi::Exception& err) { StatusTimeout(to_wx("Exception when attempting to autosave file: " + err.GetMessage())); } -catch (wxString err) { - StatusTimeout("Exception when attempting to autosave file: " + err); -} -catch (const char *err) { - StatusTimeout("Exception when attempting to autosave file: " + wxString(err)); -} catch (...) { StatusTimeout("Unhandled exception when attempting to autosave file."); } @@ -696,15 +689,15 @@ void FrameMain::OnSubtitlesOpen() { /// prompting for each file loaded/unloaded // Load stuff from the new script - wxString curSubsVideo = DecodeRelativePath(context->ass->GetScriptInfo("Video File"), context->ass->filename); - wxString curSubsVFR = DecodeRelativePath(context->ass->GetScriptInfo("VFR File"), context->ass->filename); - wxString curSubsKeyframes = DecodeRelativePath(context->ass->GetScriptInfo("Keyframes File"), context->ass->filename); - wxString curSubsAudio = DecodeRelativePath(context->ass->GetScriptInfo("Audio URI"), context->ass->filename); + auto video = config::path->MakeAbsolute(context->ass->GetScriptInfo("Video File"), "?script"); + auto vfr = config::path->MakeAbsolute(context->ass->GetScriptInfo("VFR File"), "?script"); + auto keyframes = config::path->MakeAbsolute(context->ass->GetScriptInfo("Keyframes File"), "?script"); + auto audio = config::path->MakeAbsolute(context->ass->GetScriptInfo("Audio URI"), "?script"); - bool videoChanged = !blockVideoLoad && curSubsVideo != context->videoController->GetVideoName(); - bool timecodesChanged = curSubsVFR != context->videoController->GetTimecodesName(); - bool keyframesChanged = curSubsKeyframes != context->videoController->GetKeyFramesName(); - bool audioChanged = !blockAudioLoad && curSubsAudio != context->audioController->GetAudioURL(); + bool videoChanged = !blockVideoLoad && video != context->videoController->GetVideoName(); + bool timecodesChanged = vfr != context->videoController->GetTimecodesName(); + bool keyframesChanged = keyframes != context->videoController->GetKeyFramesName(); + bool audioChanged = !blockAudioLoad && audio != context->audioController->GetAudioURL(); // Check if there is anything to change int autoLoadMode = OPT_GET("App/Auto/Load Linked Files")->GetInt(); @@ -725,43 +718,42 @@ void FrameMain::OnSubtitlesOpen() { // Video if (videoChanged) { - wxString arString = context->ass->GetScriptInfo("Video Aspect Ratio"); - context->videoController->SetVideo(curSubsVideo); + context->videoController->SetVideo(video); if (context->videoController->IsLoaded()) { context->videoController->JumpToFrame(context->ass->GetScriptInfoAsInt("Video Position")); - long videoAr = 0; + int videoAr = 0; double videoArValue = 0.; - if (arString.StartsWith("c")) { + std::string arString = context->ass->GetScriptInfo("Video Aspect Ratio"); + if (boost::starts_with(arString, "c")) { videoAr = 4; - arString.Mid(1).ToDouble(&videoArValue); + agi::util::try_parse(arString.substr(1), &videoArValue); } else - arString.ToLong(&videoAr); + agi::util::try_parse(arString.substr(1), &videoAr); context->videoController->SetAspectRatio(videoAr, videoArValue); double videoZoom = 0.; - if (context->ass->GetScriptInfo("Video Zoom Percent").ToDouble(&videoZoom)) + if (agi::util::try_parse(context->ass->GetScriptInfo("Video Zoom Percent"), &videoZoom)) context->videoDisplay->SetZoom(videoZoom); } } - context->videoController->LoadTimecodes(curSubsVFR); - context->videoController->LoadKeyframes(curSubsKeyframes); + context->videoController->LoadTimecodes(vfr); + context->videoController->LoadKeyframes(keyframes); // Audio if (audioChanged) { blockAudioLoad = false; try { - if (!curSubsAudio) + if (audio.empty()) context->audioController->CloseAudio(); else - context->audioController->OpenAudio(curSubsAudio); + context->audioController->OpenAudio(audio); } catch (agi::UserCancelException const&) { } - catch (agi::FileNotAccessibleError const& err) { - config::mru->Remove("Audio", from_wx(curSubsAudio)); + catch (agi::fs::FileSystemError const& err) { wxMessageBox(to_wx(err.GetMessage()), "Error opening audio", wxOK | wxICON_ERROR | wxCENTER, this); } } @@ -789,5 +781,5 @@ wxString FrameMain::GetScriptFileName() const { #endif } else - return wxFileName(context->ass->filename).GetFullName(); + return context->ass->filename.filename().wstring(); } diff --git a/aegisub/src/frame_main.h b/aegisub/src/frame_main.h index 2ee78ab0f..5e2a7daa7 100644 --- a/aegisub/src/frame_main.h +++ b/aegisub/src/frame_main.h @@ -32,6 +32,9 @@ /// @ingroup main_ui /// +#include + +#include #include #include @@ -42,8 +45,6 @@ #include #include -#include - class AegisubFileDropTarget; class AssFile; class AudioBox; @@ -63,7 +64,7 @@ namespace agi { struct Context; class OptionValue; } class FrameMain: public wxFrame { friend class AegisubFileDropTarget; - agi::scoped_ptr context; + std::unique_ptr context; // XXX: Make Freeze()/Thaw() noops on GTK, this seems to be buggy #ifdef __WXGTK__ @@ -137,7 +138,7 @@ public: /// @param enableCancel Should the user be able to cancel the close? int TryToCloseSubs(bool enableCancel=true); - void LoadSubtitles(wxString const& filename, wxString const& charset=""); + void LoadSubtitles(agi::fs::path const& filename, std::string const& charset=""); DECLARE_EVENT_TABLE() }; diff --git a/aegisub/src/gl_text.cpp b/aegisub/src/gl_text.cpp index 43bd695fe..2d883dc01 100644 --- a/aegisub/src/gl_text.cpp +++ b/aegisub/src/gl_text.cpp @@ -36,14 +36,17 @@ #include "gl_text.h" +#include "compat.h" +#include "utils.h" + +#include + #include #include #include #include -#include "utils.h" - #ifdef HAVE_OPENGL_GL_H #include #else @@ -231,7 +234,6 @@ OpenGLText::OpenGLText() , g(1.f) , b(1.f) , a(1.f) -, lineHeight(0) , fontSize(0) , fontBold(false) , fontItalics(false) @@ -241,7 +243,7 @@ OpenGLText::OpenGLText() OpenGLText::~OpenGLText() { } -void OpenGLText::SetFont(wxString face, int size, bool bold, bool italics) { +void OpenGLText::SetFont(std::string const& face, int size, bool bold, bool italics) { // No change required if (size == fontSize && face == fontFace && bold == fontBold && italics == fontItalics) return; @@ -250,7 +252,7 @@ void OpenGLText::SetFont(wxString face, int size, bool bold, bool italics) { fontSize = size; fontBold = bold; fontItalics = italics; - font.SetFaceName(fontFace); + font.SetFaceName(to_wx(fontFace)); font.SetPointSize(size); font.SetWeight(bold ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL); @@ -259,14 +261,14 @@ void OpenGLText::SetFont(wxString face, int size, bool bold, bool italics) { glyphs.clear(); } -void OpenGLText::SetColour(wxColour col, float alpha) { - r = col.Red() / 255.f; - g = col.Green() / 255.f; - b = col.Blue() / 255.f; - a = alpha; +void OpenGLText::SetColour(agi::Color col) { + r = col.r / 255.f; + g = col.g / 255.f; + b = col.b / 255.f; + a = col.a / 255.f; } -void OpenGLText::Print(const wxString &text, int x, int y) { +void OpenGLText::Print(const std::string &text, int x, int y) { glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -287,54 +289,22 @@ void OpenGLText::Print(const wxString &text, int x, int y) { glDisable(GL_BLEND); } -void OpenGLText::DrawString(const wxString &text, int x, int y) { - lineHeight = 0; - int dx=x, dy=y; - - for (int curChar : text) { - // Handle carriage returns - if (curChar == '\n') { - dx = x; - dy += lineHeight; - } - - // Handle normal glyphs - else { - OpenGLTextGlyph const& glyph = GetGlyph(curChar); - glyph.Draw(dx, dy); - dx += glyph.w; - if (glyph.h > lineHeight) lineHeight = glyph.h; - } +void OpenGLText::DrawString(const std::string &text, int x, int y) { + for (char curChar : text) { + OpenGLTextGlyph const& glyph = GetGlyph(curChar); + glyph.Draw(x, y); + x += glyph.w; } } -void OpenGLText::GetExtent(wxString const& text, int &w, int &h) { - lineHeight = 0; - int dx=0, dy=0; - w = 0; - h = 0; +void OpenGLText::GetExtent(std::string const& text, int &w, int &h) { + w = h = 0; - // Simulate drawing of string - for (int curChar : text) { - // Handle carriage returns - if (curChar == '\n') { - if (dx > w) w = dx; - dx = 0; - dy += lineHeight; - lineHeight = 0; - } - - // Handle normal glyphs - else { - OpenGLTextGlyph const& glyph = GetGlyph(curChar); - dx += glyph.w; - if (glyph.h > lineHeight) lineHeight = glyph.h; - } + for (char curChar : text) { + OpenGLTextGlyph const& glyph = GetGlyph(curChar); + w += glyph.w; + h = std::max(h, glyph.h); } - - // Return results - if (dx > w) w = dx; - h = dy+lineHeight; } OpenGLTextGlyph const& OpenGLText::GetGlyph(int i) { diff --git a/aegisub/src/gl_text.h b/aegisub/src/gl_text.h index c1870ee40..dbb0a3991 100644 --- a/aegisub/src/gl_text.h +++ b/aegisub/src/gl_text.h @@ -35,9 +35,9 @@ #include #include +#include #include -#include #include namespace { @@ -45,16 +45,17 @@ struct OpenGLTextGlyph; class OpenGLTextTexture; } -typedef boost::container::map glyphMap; +namespace agi { struct Color; } + +typedef boost::container::map glyphMap; class OpenGLText { float r,g,b,a; - int lineHeight; int fontSize; bool fontBold; bool fontItalics; - wxString fontFace; + std::string fontFace; wxFont font; glyphMap glyphs; @@ -71,7 +72,7 @@ class OpenGLText { /// @brief Create a new glyph OpenGLTextGlyph const& CreateGlyph(int chr); - void DrawString(const wxString &text,int x,int y); + void DrawString(const std::string &text,int x,int y); public: /// @brief Get the currently active font wxFont GetFont() const { return font; } @@ -81,21 +82,20 @@ public: /// @param size Size in points of the desired font /// @param bold Should the font be bold? /// @param italics Should the font be italic? - void SetFont(wxString face,int size,bool bold,bool italics); + void SetFont(std::string const& face, int size, bool bold, bool italics); /// @brief Set the text color /// @param col Color - /// @param alpha Alpha value from 0.f-1.f - void SetColour(wxColour col,float alpha); + void SetColour(agi::Color col); /// @brief Print a string on screen /// @param text String to print /// @param x x coordinate /// @param y y coordinate - void Print(const wxString &text,int x,int y); + void Print(const std::string &text,int x,int y); /// @brief Get the extents of a string printed with the current font in pixels /// @param text String to get extends of /// @param[out] w Width /// @param[out] h Height - void GetExtent(const wxString &text,int &w,int &h); + void GetExtent(const std::string &text,int &w,int &h); OpenGLText(); ~OpenGLText(); diff --git a/aegisub/src/gl_wrap.cpp b/aegisub/src/gl_wrap.cpp index 9df8ee707..03949b4df 100644 --- a/aegisub/src/gl_wrap.cpp +++ b/aegisub/src/gl_wrap.cpp @@ -24,7 +24,7 @@ #include "gl_wrap.h" -#include +#include #ifdef HAVE_OPENGL_GL_H #include diff --git a/aegisub/src/help_button.cpp b/aegisub/src/help_button.cpp index 2030e2594..1b277837e 100644 --- a/aegisub/src/help_button.cpp +++ b/aegisub/src/help_button.cpp @@ -36,18 +36,16 @@ #include "help_button.h" -#include +#include "standard_paths.h" + +#include #include #include #include -#include #include -#include "standard_paths.h" -#include "utils.h" - static std::map *pages = 0; static void init_static() { @@ -94,7 +92,7 @@ void HelpButton::OpenPage(wxString const& pageID) { wxString section; page = page.BeforeFirst('#', §ion); - wxFileName docFile(StandardPaths::DecodePath("?data/docs/"), page, "html", wxPATH_NATIVE); + wxFileName docFile(StandardPaths::DecodePath("?data/docs/").wstring(), page, "html", wxPATH_NATIVE); wxString url; // If we can read a file by the constructed name, assume we have a local copy of the manual diff --git a/aegisub/src/hotkey.cpp b/aegisub/src/hotkey.cpp index 6431985cc..9375c52b0 100644 --- a/aegisub/src/hotkey.cpp +++ b/aegisub/src/hotkey.cpp @@ -24,7 +24,6 @@ #include "libresrc/libresrc.h" #include "command/command.h" -#include "compat.h" #include "options.h" #include "standard_paths.h" @@ -124,7 +123,7 @@ namespace hotkey { agi::hotkey::Hotkey *inst = 0; void init() { inst = new agi::hotkey::Hotkey( - from_wx(StandardPaths::DecodePath("?user/hotkey.json")), + StandardPaths::DecodePath("?user/hotkey.json"), GET_DEFAULT_CONFIG(default_hotkey)); int last_version = OPT_GET("Version/Last Version")->GetInt(); diff --git a/aegisub/src/hotkey_data_view_model.cpp b/aegisub/src/hotkey_data_view_model.cpp index f72e3b342..b0ae35c45 100644 --- a/aegisub/src/hotkey_data_view_model.cpp +++ b/aegisub/src/hotkey_data_view_model.cpp @@ -90,13 +90,13 @@ public: void GetValue(wxVariant &variant, unsigned int col) const { if (col == 0) - variant = combo.Str(); + variant = to_wx(combo.Str()); else if (col == 1) { wxBitmap icon_bmp(icon::get(combo.CmdName(), 16)); wxIcon icon; if (icon_bmp.IsOk()) icon.CopyFromBitmap(icon_bmp); - variant << wxDataViewIconText(combo.CmdName(), icon); + variant << wxDataViewIconText(to_wx(combo.CmdName()), icon); } else if (col == 2) { try { @@ -117,7 +117,7 @@ public: keys.reserve(toks.size()); transform(toks.begin(), toks.end(), back_inserter(keys), (std::string(*)(wxString const&))&from_wx); combo = Combo(combo.Context(), combo.CmdName(), keys); - cmd_str = combo.Str(); + cmd_str = to_wx(combo.Str()); return true; } else if (col == 1) { @@ -226,7 +226,7 @@ public: if (cat_it != cat_map.end()) cat = cat_it->second; else { - categories.emplace_back(model, cat_name); + categories.emplace_back(model, to_wx(cat_name)); cat = cat_map[cat_name] = &categories.back(); } diff --git a/aegisub/src/include/aegisub/audio_provider.h b/aegisub/src/include/aegisub/audio_provider.h index a91ce4b73..855e2c301 100644 --- a/aegisub/src/include/aegisub/audio_provider.h +++ b/aegisub/src/include/aegisub/audio_provider.h @@ -34,10 +34,12 @@ #pragma once -#include +#include "factory_manager.h" #include -#include "factory_manager.h" +#include + +#include class AudioProvider { protected: @@ -48,7 +50,7 @@ protected: int sample_rate; int bytes_per_sample; bool float_samples; - wxString filename; + agi::fs::path filename; virtual void FillBuffer(void *buf, int64_t start, int64_t count) const = 0; @@ -60,27 +62,25 @@ public: void GetAudio(void *buf, int64_t start, int64_t count) const; void GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const; - wxString GetFilename() const { return filename; }; - int64_t GetNumSamples() const { return num_samples; } - int GetSampleRate() const { return sample_rate; } - int GetBytesPerSample() const { return bytes_per_sample; } - int GetChannels() const { return channels; } - bool AreSamplesFloat() const { return float_samples; } - virtual bool AreSamplesNativeEndian() const = 0; - - virtual int64_t GetSamplesLoaded() const { return num_samples; } + agi::fs::path GetFilename() const { return filename; } + int64_t GetNumSamples() const { return num_samples; } + int GetSampleRate() const { return sample_rate; } + int GetBytesPerSample() const { return bytes_per_sample; } + int GetChannels() const { return channels; } + bool AreSamplesFloat() const { return float_samples; } /// @brief Does this provider benefit from external caching? virtual bool NeedsCache() const { return false; } + virtual bool AreSamplesNativeEndian() const = 0; }; -class AudioProviderFactory : public Factory1 { +class AudioProviderFactory : public Factory1 { public: static void RegisterProviders(); /// Get a provider for the file /// @param filename URI to open - static AudioProvider *GetProvider(wxString const& filename); + static AudioProvider *GetProvider(agi::fs::path const& filename); }; DEFINE_BASE_EXCEPTION_NOINNER(AudioProviderError, agi::Exception) diff --git a/aegisub/src/include/aegisub/video_provider.h b/aegisub/src/include/aegisub/video_provider.h index aabfbc8aa..e6f2a2cdb 100644 --- a/aegisub/src/include/aegisub/video_provider.h +++ b/aegisub/src/include/aegisub/video_provider.h @@ -37,7 +37,7 @@ #include #include -#include +#include class AegiVideoFrame; @@ -59,13 +59,13 @@ public: /// Get the source colorspace of the video before it was converted to RGB /// @return A string describing the source colorspace or "None" if it is /// unknown or meaningless - virtual wxString GetColorSpace() const = 0; + virtual std::string GetColorSpace() const = 0; /// @brief Use this to set any post-loading warnings, such as "being loaded with unreliable seeking" - virtual wxString GetWarning() const { return ""; } + virtual std::string GetWarning() const { return ""; } /// @brief Name of decoder, e.g. "Avisynth/FFMpegSource" - virtual wxString GetDecoderName() const = 0; + virtual std::string GetDecoderName() const = 0; /// @brief Does this provider want Aegisub to cache video frames? /// @return Returns true if caching is desired, false otherwise. diff --git a/aegisub/src/initial_line_state.h b/aegisub/src/initial_line_state.h index 1230bab0a..a92724b34 100644 --- a/aegisub/src/initial_line_state.h +++ b/aegisub/src/initial_line_state.h @@ -12,26 +12,24 @@ // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -#ifndef AGI_PRE -#include -#endif - #include +#include + namespace agi { struct Context; } class AssDialogue; class InitialLineState { agi::signal::Connection active_line_connection; - wxString initial_text; + std::string initial_text; int line_id; - agi::signal::Signal InitialStateChanged; + agi::signal::Signal InitialStateChanged; void OnActiveLineChanged(AssDialogue *new_line); public: InitialLineState(agi::Context *c); - wxString GetInitialText() const { return initial_text; } + std::string const& GetInitialText() const { return initial_text; } DEFINE_SIGNAL_ADDERS(InitialStateChanged, AddChangeListener); }; diff --git a/aegisub/src/main.cpp b/aegisub/src/main.cpp index e45983b83..38a42d2e6 100644 --- a/aegisub/src/main.cpp +++ b/aegisub/src/main.cpp @@ -34,24 +34,18 @@ #include "config.h" -#include "main.h" - -#include "include/aegisub/menu.h" #include "command/command.h" #include "command/icon.h" -#include "include/aegisub/toolbar.h" #include "include/aegisub/hotkey.h" #include "ass_dialogue.h" -#include "ass_export_filter.h" #include "ass_file.h" -#include "audio_box.h" #include "auto4_base.h" -#include "charset_conv.h" #include "compat.h" #include "export_fixstyle.h" #include "export_framerate.h" #include "frame_main.h" +#include "main.h" #include "libresrc/libresrc.h" #include "options.h" #include "plugin_manager.h" @@ -61,25 +55,27 @@ #include "video_context.h" #include "utils.h" +#include +#include +#include #include #include -#include -#include +#include +#include +#include +#include #include -#include #include -#include -#include -#include #include -#include +#include #include namespace config { - agi::Options *opt = 0; - agi::MRUManager *mru = 0; + agi::Options *opt = nullptr; + agi::MRUManager *mru = nullptr; + agi::Path *path = nullptr; } IMPLEMENT_APP(AegisubApp) @@ -133,6 +129,8 @@ AegisubApp::AegisubApp() { wxSetEnv("UBUNTU_MENUPROXY", "0"); } +wxDEFINE_EVENT(EVT_CALL_THUNK, wxThreadEvent); + /// @brief Gets called when application starts. /// @return bool bool AegisubApp::OnInit() { @@ -143,13 +141,20 @@ bool AegisubApp::OnInit() { SetAppName("aegisub"); #endif - Bind(EVT_CALL_THUNK, [](wxThreadEvent &evt) { + // Pointless `this` capture required due to http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51494 + agi::dispatch::Init([this](agi::dispatch::Thunk f) { + wxThreadEvent *evt = new wxThreadEvent(EVT_CALL_THUNK); + evt->SetPayload(f); + wxTheApp->QueueEvent(evt); + }); + + wxTheApp->Bind(EVT_CALL_THUNK, [](wxThreadEvent &evt) { evt.GetPayload>()(); }); - // logging. - agi::log::log = new agi::log::LogSink; + config::path = new agi::Path; + agi::log::log = new agi::log::LogSink; #ifdef _DEBUG agi::log::log->Subscribe(new agi::log::EmitSTDOUT()); #endif @@ -159,29 +164,29 @@ bool AegisubApp::OnInit() { #ifdef __WXMSW__ // Try loading configuration from the install dir if one exists there try { - std::string conf_local(from_wx(StandardPaths::DecodePath("?data/config.json"))); - agi::scoped_ptr localConfig(agi::io::Open(conf_local)); + auto conf_local(StandardPaths::DecodePath("?data/config.json")); + std::unique_ptr localConfig(agi::io::Open(conf_local)); config::opt = new agi::Options(conf_local, GET_DEFAULT_CONFIG(default_config)); // Local config, make ?user mean ?data so all user settings are placed in install dir StandardPaths::SetPathValue("?user", StandardPaths::DecodePath("?data")); StandardPaths::SetPathValue("?local", StandardPaths::DecodePath("?data")); - } catch (agi::FileNotAccessibleError const&) { + } catch (agi::fs::FileSystemError const&) { // File doesn't exist or we can't read it // Might be worth displaying an error in the second case } #endif StartupLog("Create log writer"); - wxString path_log = StandardPaths::DecodePath("?user/log/"); - wxFileName::Mkdir(path_log, 0777, wxPATH_MKDIR_FULL); - agi::log::log->Subscribe(new agi::log::JsonEmitter(from_wx(path_log), agi::log::log)); + auto path_log = StandardPaths::DecodePath("?user/log/"); + agi::fs::CreateDirectory(path_log); + agi::log::log->Subscribe(new agi::log::JsonEmitter(path_log, agi::log::log)); CleanCache(path_log, "*.json", 10, 100); StartupLog("Load user configuration"); try { if (!config::opt) - config::opt = new agi::Options(from_wx(StandardPaths::DecodePath("?user/config.json")), GET_DEFAULT_CONFIG(default_config)); + config::opt = new agi::Options(StandardPaths::DecodePath("?user/config.json"), GET_DEFAULT_CONFIG(default_config)); std::istringstream stream(GET_DEFAULT_CONFIG(default_config_platform)); config::opt->ConfigNext(stream); } catch (agi::Exception& e) { @@ -205,7 +210,7 @@ bool AegisubApp::OnInit() { icon::icon_init(); StartupLog("Load MRU"); - config::mru = new agi::MRUManager(from_wx(StandardPaths::DecodePath("?user/mru.json")), GET_DEFAULT_CONFIG(default_mru), config::opt); + config::mru = new agi::MRUManager(StandardPaths::DecodePath("?user/mru.json"), GET_DEFAULT_CONFIG(default_mru), config::opt); SetThreadName(-1, "AegiMain"); @@ -246,7 +251,7 @@ bool AegisubApp::OnInit() { // Load Automation scripts StartupLog("Load global Automation scripts"); - global_scripts = new Automation4::AutoloadScriptManager(to_wx(OPT_GET("Path/Automation/Autoload")->GetString())); + global_scripts = new Automation4::AutoloadScriptManager(OPT_GET("Path/Automation/Autoload")->GetString()); // Load export filters StartupLog("Register export filters"); @@ -286,8 +291,7 @@ bool AegisubApp::OnInit() { #endif StartupLog("Clean old autosave files"); - wxString autosave_path = StandardPaths::DecodePath(to_wx(OPT_GET("Path/Auto/Save")->GetString())); - CleanCache(autosave_path, "*.AUTOSAVE.ass", 100, 1000); + CleanCache(StandardPaths::DecodePath(OPT_GET("Path/Auto/Save")->GetString()), "*.AUTOSAVE.ass", 100, 1000); StartupLog("Initialization complete"); return true; @@ -298,7 +302,6 @@ int AegisubApp::OnExit() { delete frame; SubtitleFormat::DestroyFormats(); - VideoContext::OnExit(); delete plugins; delete config::opt; delete config::mru; @@ -317,50 +320,44 @@ int AegisubApp::OnExit() { #if wxUSE_STACKWALKER == 1 class StackWalker: public wxStackWalker { - wxFile *crash_text; // FP to the crash text file. + boost::filesystem::ofstream fp; public: - StackWalker(wxString cause); + StackWalker(std::string const& cause); ~StackWalker(); - void OnStackFrame(const wxStackFrame& frame); + void OnStackFrame(wxStackFrame const& frame); }; /// @brief Called at the start of walking the stack. /// @param cause cause of the crash. -/// -StackWalker::StackWalker(wxString cause) { - crash_text = new wxFile(StandardPaths::DecodePath("?user/crashlog.txt"), wxFile::write_append); +StackWalker::StackWalker(std::string const& cause) +: fp(StandardPaths::DecodePath("?user/crashlog.txt"), std::ios::app) +{ + if (!fp.good()) return; - if (crash_text->IsOpened()) { - wxDateTime time = wxDateTime::Now(); - - crash_text->Write(wxString::Format("--- %s ------------------\n", time.FormatISOCombined())); - crash_text->Write(wxString::Format("VER - %s\n", GetAegisubLongVersionString())); - crash_text->Write(wxString::Format("FTL - Beginning stack dump for \"%s\":\n", cause)); - } + fp << agi::util::strftime("--- %y-%m-%d %H:%M:%S ------------------\n"); + fp << boost::format("VER - %s\n") % GetAegisubLongVersionString(); + fp << boost::format("FTL - Beginning stack dump for \"%s\": \n") % cause; } /// @brief Callback to format a single frame /// @param frame frame to parse. -/// -void StackWalker::OnStackFrame(const wxStackFrame &frame) { - if (crash_text->IsOpened()) { - wxString dst = wxString::Format("%03u - %p: ", (unsigned)frame.GetLevel(),frame.GetAddress()) + frame.GetName(); - if (frame.HasSourceLocation()) - dst = wxString::Format("%s on %s:%u", dst, frame.GetFileName(), (unsigned)frame.GetLine()); +void StackWalker::OnStackFrame(wxStackFrame const& frame) { + if (!fp.good()) return; - crash_text->Write(wxString::Format("%s\n", dst)); - } + fp << boost::format("%03u - %p: %s") % frame.GetLevel() % frame.GetAddress(); + if (frame.HasSourceLocation()) + fp << boost::format(" on %s:%u") % frame.GetFileName(), frame.GetLine(); + + fp << "\n"; } /// @brief Called at the end of walking the stack. StackWalker::~StackWalker() { - if (crash_text->IsOpened()) { - crash_text->Write("End of stack dump.\n"); - crash_text->Write("----------------------------------------\n\n"); + if (!fp.good()) return; - crash_text->Close(); - } + fp << "End of stack dump.\n"; + fp << "----------------------------------------\n\n"; } #endif @@ -370,17 +367,12 @@ const static wxString exception_message = _("Oops, Aegisub has crashed!\n\nAn at static void UnhandledExeception(bool stackWalk) { #if (!defined(_DEBUG) || defined(WITH_EXCEPTIONS)) && (wxUSE_ON_FATAL_EXCEPTION+0) if (AssFile::top) { - // Current filename if any. - wxFileName file(AssFile::top->filename); - if (!file.HasName()) file.SetName("untitled"); + auto path = StandardPaths::DecodePath("?user/recovered"); + agi::fs::CreateDirectory(path); - // Set path and create if it doesn't exist. - file.SetPath(StandardPaths::DecodePath("?user/recovered")); - if (!file.DirExists()) file.Mkdir(); - - // Save file - wxString filename = wxString::Format("%s/%s.%s.ass", file.GetPath(), file.GetName(), wxDateTime::Now().Format("%Y-%m-%d-%H-%M-%S")); - AssFile::top->Save(filename,false,false); + auto filename = AssFile::top->filename.empty() ? "untitled" : AssFile::top->filename.stem().string(); + path /= str(boost::format("%s.%s.ass") % filename % agi::util::strftime("%Y-%m-%d-%H-%M-%S")); + AssFile::top->Save(path, false, false); #if wxUSE_STACKWALKER == 1 if (stackWalk) { @@ -390,7 +382,7 @@ static void UnhandledExeception(bool stackWalk) { #endif // Inform user of crash. - wxMessageBox(wxString::Format(exception_message, filename), _("Program error"), wxOK | wxICON_ERROR | wxCENTER, nullptr); + wxMessageBox(wxString::Format(exception_message, path.wstring()), _("Program error"), wxOK | wxICON_ERROR | wxCENTER, nullptr); } else if (LastStartupState) { #if wxUSE_STACKWALKER == 1 @@ -404,12 +396,10 @@ static void UnhandledExeception(bool stackWalk) { #endif } -/// @brief Called during an unhandled exception void AegisubApp::OnUnhandledException() { UnhandledExeception(false); } -/// @brief Called during a fatal exception. void AegisubApp::OnFatalException() { UnhandledExeception(true); } @@ -423,10 +413,10 @@ void AegisubApp::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEven SHOW_EXCEPTION(to_wx(e.GetChainedMessage())); } catch (const std::exception &e) { - SHOW_EXCEPTION(wxString(e.what(), wxConvUTF8)); + SHOW_EXCEPTION(to_wx(e.what())); } catch (const char *e) { - SHOW_EXCEPTION(wxString(e, wxConvUTF8)); + SHOW_EXCEPTION(to_wx(e)); } catch (const wxString &e) { SHOW_EXCEPTION(e); @@ -435,33 +425,26 @@ void AegisubApp::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEven } int AegisubApp::OnRun() { - wxString error; + std::string error; - // Run program try { if (m_exitOnFrameDelete == Later) m_exitOnFrameDelete = Yes; return MainLoop(); } - - // Catch errors - catch (const wxString &err) { error = err; } + catch (const wxString &err) { error = from_wx(err); } catch (const char *err) { error = err; } - catch (const std::exception &e) { error = wxString("std::exception: ") + wxString(e.what(),wxConvUTF8); } - catch (const agi::Exception &e) { error = "agi::exception: " + to_wx(e.GetChainedMessage()); } + catch (const std::exception &e) { error = std::string("std::exception: ") + e.what(); } + catch (const agi::Exception &e) { error = "agi::exception: " + e.GetChainedMessage(); } catch (...) { error = "Program terminated in error."; } // Report errors - if (!error.IsEmpty()) { - std::ofstream file; - file.open(wxString(StandardPaths::DecodePath("?user/crashlog.txt")).mb_str(),std::ios::out | std::ios::app); + if (!error.empty()) { + boost::filesystem::ofstream file(StandardPaths::DecodePath("?user/crashlog.txt"), std::ios::app); if (file.is_open()) { - wxDateTime time = wxDateTime::Now(); - wxString timeStr = "---" + time.FormatISODate() + " " + time.FormatISOTime() + "------------------"; - file << std::endl << timeStr.mb_str(csConvLocal); - file << "\nVER - " << GetAegisubLongVersionString(); - file << "\nEXC - Aegisub has crashed with unhandled exception \"" << error.mb_str(csConvLocal) <<"\".\n"; - file << wxString('-', timeStr.size()); - file << "\n"; + file << agi::util::strftime("--- %y-%m-%d %H:%M:%S ------------------\n"); + file << boost::format("VER - %s\n") % GetAegisubLongVersionString(); + file << boost::format("EXC - Aegisub has crashed with unhandled exception \"%s\".\n") % error; + file << "----------------------------------------\n\n"; file.close(); } @@ -474,7 +457,7 @@ int AegisubApp::OnRun() { void AegisubApp::MacOpenFile(const wxString &filename) { if (frame != nullptr && !filename.empty()) { - frame->LoadSubtitles(filename); + frame->LoadSubtitles(from_wx(filename)); wxFileName filepath(filename); OPT_SET("Path/Last/Subtitles")->SetString(from_wx(filepath.GetPath())); } diff --git a/aegisub/src/main.h b/aegisub/src/main.h index 20c137804..03661e549 100644 --- a/aegisub/src/main.h +++ b/aegisub/src/main.h @@ -33,8 +33,6 @@ /// #include -#include -#include #include "aegisublocale.h" diff --git a/aegisub/src/menu.cpp b/aegisub/src/menu.cpp index f968b13b9..407db65a3 100644 --- a/aegisub/src/menu.cpp +++ b/aegisub/src/menu.cpp @@ -37,11 +37,9 @@ #include #include -#include #include #include -#include #include #include #include @@ -98,9 +96,12 @@ public: int i = 0; for (auto it = mru->begin(); it != mru->end(); ++it, ++i) { + wxString name = it->wstring(); + if (!name.StartsWith("?")) + name = it->filename().wstring(); items[i]->SetItemLabel(wxString::Format("%s%d %s", i <= 9 ? "&" : "", i + 1, - wxFileName(to_wx(*it)).GetFullName())); + name)); items[i]->Enable(true); } } @@ -165,7 +166,7 @@ class CommandManager { text = c->StrMenu(context); else text = item.second->GetItemLabel().BeforeFirst('\t'); - item.second->SetItemLabel(text + "\t" + hotkey::get_hotkey_str_first("Default", c->name())); + item.second->SetItemLabel(text + to_wx("\t" + hotkey::get_hotkey_str_first("Default", c->name()))); } public: @@ -184,7 +185,7 @@ public: wxITEM_NORMAL; wxString menu_text = text.empty() ? co->StrMenu(context) : _(to_wx(text)); - menu_text += "\t" + hotkey::get_hotkey_str_first("Default", co->name()); + menu_text += to_wx("\t" + hotkey::get_hotkey_str_first("Default", co->name())); wxMenuItem *item = new wxMenuItem(parent, MENU_ID_BASE + items.size(), menu_text, co->StrHelp(), kind); #ifndef __WXMAC__ @@ -292,7 +293,7 @@ menu_map const& get_menus_root() { if (!root.empty()) return root; try { - root = agi::json_util::file(StandardPaths::DecodePath("?user/menu.json").utf8_str().data(), GET_DEFAULT_CONFIG(default_menu)); + root = agi::json_util::file(StandardPaths::DecodePath("?user/menu.json"), GET_DEFAULT_CONFIG(default_menu)); return root; } catch (json::Reader::ParseException const& e) { diff --git a/aegisub/src/mkv_wrap.cpp b/aegisub/src/mkv_wrap.cpp index 978284d12..93d39e35a 100644 --- a/aegisub/src/mkv_wrap.cpp +++ b/aegisub/src/mkv_wrap.cpp @@ -34,16 +34,6 @@ #include "config.h" -#include -#include -#include -#include -#include - -#include -#include -#include // Keep this last so wxUSE_CHOICEDLG is set. - #include "mkv_wrap.h" #include "ass_file.h" @@ -53,9 +43,26 @@ #include "dialog_progress.h" #include "MatroskaParser.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // Keep this last so wxUSE_CHOICEDLG is set. + class MkvStdIO : public InputStream { public: - MkvStdIO(wxString filename); + MkvStdIO(agi::fs::path const& filename); ~MkvStdIO() { if (fp) fclose(fp); } FILE *fp; @@ -73,32 +80,20 @@ public: #endif static void read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO *input, bool srt, double totalTime, AssParser *parser) { - std::map subList; - char *readBuf = 0; - size_t readBufSize = 0; + std::map subList; + std::string readBuf; // Load blocks ulonglong startTime, endTime, filePos; unsigned int rt, frameSize, frameFlags; - while (mkv_ReadFrame(file,0,&rt,&startTime,&endTime,&filePos,&frameSize,&frameFlags) == 0) { - if (ps->IsCancelled()) { - delete[] readBuf; - return; - } - - // Read to temp - if (frameSize > readBufSize) { - delete[] readBuf; - readBufSize = frameSize * 2; - readBuf = new char[readBufSize]; - } - else if (frameSize == 0) - continue; + while (mkv_ReadFrame(file, 0, &rt, &startTime, &endTime, &filePos, &frameSize, &frameFlags) == 0) { + if (ps->IsCancelled()) return; + if (frameSize == 0) continue; + readBuf.resize(frameSize); std_fseek(input->fp, filePos, SEEK_SET); - fread(readBuf, 1, frameSize, input->fp); - wxString blockString(readBuf, wxConvUTF8, frameSize); + fread(&readBuf[0], 1, frameSize, input->fp); // Get start and end times longlong timecodeScaleLow = 1000000; @@ -107,35 +102,35 @@ static void read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO * // Process SSA/ASS if (!srt) { - long order = 0, layer = 0; - wxString afterOrder, afterLayer; - blockString.BeforeFirst(',', &afterOrder).ToLong(&order); - afterOrder.BeforeFirst(',', &afterLayer).ToLong(&layer); + std::vector> chunks; + boost::split(chunks, readBuf, boost::is_any_of(",")); - subList[order] = wxString::Format("Dialogue: %d,%s,%s,%s", (int)layer, subStart.GetAssFormated(), subEnd.GetAssFormated(), afterLayer); + subList[boost::lexical_cast(chunks[0])] = + str(boost::format("Dialogue: %d,%s,%s,%s") + % boost::lexical_cast(chunks[1]) + % subStart.GetAssFormated() + % subEnd.GetAssFormated() + % boost::make_iterator_range(begin(chunks[2]), readBuf.end())); } // Process SRT else { - blockString = wxString::Format("Dialogue: 0,%s,%s,Default,,0,0,0,,%s", subStart.GetAssFormated(), subEnd.GetAssFormated(), blockString); - blockString.Replace("\r\n","\\N"); - blockString.Replace("\r","\\N"); - blockString.Replace("\n","\\N"); + readBuf = str(boost::format("Dialogue: 0,%s,%s,Default,,0,0,0,,%s") % subStart.GetAssFormated() % subEnd.GetAssFormated() % readBuf); + boost::replace_all(readBuf, "\r\n", "\\N"); + boost::replace_all(readBuf, "\r", "\\N"); + boost::replace_all(readBuf, "\n", "\\N"); - subList[subList.size()] = blockString; + subList[subList.size()] = readBuf; } ps->SetProgress(startTime / timecodeScaleLow, totalTime); } - delete[] readBuf; - // Insert into file - for (auto order_value_pair : subList) { + for (auto order_value_pair : subList) parser->AddLine(order_value_pair.second); - } } -void MatroskaWrapper::GetSubtitles(wxString const& filename, AssFile *target) { +void MatroskaWrapper::GetSubtitles(agi::fs::path const& filename, AssFile *target) { MkvStdIO input(filename); char err[2048]; MatroskaFile *file = mkv_Open(&input, err, sizeof(err)); @@ -144,26 +139,20 @@ void MatroskaWrapper::GetSubtitles(wxString const& filename, AssFile *target) { try { // Get info unsigned tracks = mkv_GetNumTracks(file); - TrackInfo *trackInfo; std::vector tracksFound; - wxArrayString tracksNames; + std::vector tracksNames; unsigned trackToRead; // Find tracks for (unsigned track = 0; track < tracks; track++) { - trackInfo = mkv_GetTrackInfo(file,track); + TrackInfo *trackInfo = mkv_GetTrackInfo(file, track); + if (trackInfo->Type != 0x11) continue; - // Subtitle track - if (trackInfo->Type == 0x11) { - wxString CodecID = wxString::FromUTF8(trackInfo->CodecID); - wxString TrackName = wxString::FromUTF8(trackInfo->Name); - wxString TrackLanguage = wxString::FromUTF8(trackInfo->Language); - - // Known subtitle format - if (CodecID == "S_TEXT/SSA" || CodecID == "S_TEXT/ASS" || CodecID == "S_TEXT/UTF8") { - tracksFound.push_back(track); - tracksNames.Add(wxString::Format("%d (%s %s): %s", track, CodecID, TrackLanguage, TrackName)); - } + // Known subtitle format + std::string CodecID(trackInfo->CodecID); + if (CodecID == "S_TEXT/SSA" || CodecID == "S_TEXT/ASS" || CodecID == "S_TEXT/UTF8") { + tracksFound.push_back(track); + tracksNames.emplace_back(str(boost::format("%d (%s %s): %s") % track % CodecID % trackInfo->Language % trackInfo->Name)); } } @@ -172,12 +161,11 @@ void MatroskaWrapper::GetSubtitles(wxString const& filename, AssFile *target) { throw MatroskaException("File has no recognised subtitle tracks."); // Only one track found - if (tracksFound.size() == 1) { + if (tracksFound.size() == 1) trackToRead = tracksFound[0]; - } // Pick a track else { - int choice = wxGetSingleChoiceIndex(_("Choose which track to read:"), _("Multiple subtitle tracks found"), tracksNames); + int choice = wxGetSingleChoiceIndex(_("Choose which track to read:"), _("Multiple subtitle tracks found"), to_wx(tracksNames)); if (choice == -1) throw agi::UserCancelException("canceled"); @@ -186,8 +174,8 @@ void MatroskaWrapper::GetSubtitles(wxString const& filename, AssFile *target) { // Picked track mkv_SetTrackMask(file, ~(1 << trackToRead)); - trackInfo = mkv_GetTrackInfo(file,trackToRead); - wxString CodecID = wxString::FromUTF8(trackInfo->CodecID); + TrackInfo *trackInfo = mkv_GetTrackInfo(file, trackToRead); + std::string CodecID(trackInfo->CodecID); bool srt = CodecID == "S_TEXT/UTF8"; bool ssa = CodecID == "S_TEXT/SSA"; @@ -196,13 +184,12 @@ void MatroskaWrapper::GetSubtitles(wxString const& filename, AssFile *target) { // Read private data if it's ASS/SSA if (!srt) { // Read raw data - trackInfo = mkv_GetTrackInfo(file,trackToRead); - wxString privString((const char *)trackInfo->CodecPrivate, wxConvUTF8, trackInfo->CodecPrivateSize); + std::string priv((const char *)trackInfo->CodecPrivate, trackInfo->CodecPrivateSize); // Load into file - wxStringTokenizer token(privString, "\r\n", wxTOKEN_STRTOK); - while (token.HasMoreTokens()) - parser.AddLine(token.GetNextToken()); + boost::char_separator sep("\r\n"); + for (auto const& cur : boost::tokenizer>(priv, sep)) + parser.AddLine(cur); } // Load default if it's SRT else { @@ -225,20 +212,20 @@ void MatroskaWrapper::GetSubtitles(wxString const& filename, AssFile *target) { } } -bool MatroskaWrapper::HasSubtitles(wxString const& filename) { +bool MatroskaWrapper::HasSubtitles(agi::fs::path const& filename) { char err[2048]; try { MkvStdIO input(filename); - MatroskaFile* file = mkv_Open(&input, err, sizeof(err)); + auto file = mkv_Open(&input, err, sizeof(err)); if (!file) return false; // Find tracks - int tracks = mkv_GetNumTracks(file); - for (int track = 0; track < tracks; track++) { - TrackInfo *trackInfo = mkv_GetTrackInfo(file, track); + auto tracks = mkv_GetNumTracks(file); + for (auto track : boost::irange(0u, tracks)) { + auto trackInfo = mkv_GetTrackInfo(file, track); if (trackInfo->Type == 0x11) { - wxString CodecID = wxString::FromUTF8(trackInfo->CodecID); + std::string CodecID(trackInfo->CodecID); if (CodecID == "S_TEXT/SSA" || CodecID == "S_TEXT/ASS" || CodecID == "S_TEXT/UTF8") { mkv_Close(file); return true; @@ -256,102 +243,70 @@ bool MatroskaWrapper::HasSubtitles(wxString const& filename) { } int StdIoRead(InputStream *_st, ulonglong pos, void *buffer, int count) { - MkvStdIO *st = (MkvStdIO *) _st; - size_t rd; - if (std_fseek(st->fp, pos, SEEK_SET)) { - st->error = errno; - return -1; - } - rd = fread(buffer, 1, count, st->fp); - if (rd == 0) { - if (feof(st->fp)) - return 0; - st->error = errno; - return -1; - } - return rd; + auto *st = static_cast(_st); + if (std_fseek(st->fp, pos, SEEK_SET)) { + st->error = errno; + return -1; + } + + auto rd = fread(buffer, 1, count, st->fp); + if (rd == 0) { + if (feof(st->fp)) + return 0; + st->error = errno; + return -1; + } + return rd; } /// @brief scan for a signature sig(big-endian) starting at file position pos /// @return position of the first byte of signature or -1 if error/not found -longlong StdIoScan(InputStream *_st, ulonglong start, unsigned signature) { - MkvStdIO *st = (MkvStdIO *) _st; - int c; - unsigned cmp = 0; - FILE *fp = st->fp; +longlong StdIoScan(InputStream *st, ulonglong start, unsigned signature) { + FILE *fp = static_cast(st)->fp; - if (std_fseek(fp, start, SEEK_SET)) - return -1; + if (std_fseek(fp, start, SEEK_SET)) + return -1; - while ((c = getc(fp)) != EOF) { - cmp = ((cmp << 8) | c) & 0xffffffff; - if (cmp == signature) - return std_ftell(fp) - 4; - } + int c; + unsigned cmp = 0; + while ((c = getc(fp)) != EOF) { + cmp = ((cmp << 8) | c) & 0xffffffff; + if (cmp == signature) + return std_ftell(fp) - 4; + } - return -1; + return -1; } -/// @brief This is used to limit readahead. -unsigned StdIoGetCacheSize(InputStream *st) { - return CACHESIZE; -} - -/// @brief Get last error message -const char *StdIoGetLastError(InputStream *st) { - return strerror(((MkvStdIO *)st)->error); -} - -/// @brief Memory allocation, this is done via stdlib -void *StdIoMalloc(InputStream *, size_t size) { - return malloc(size); -} - -void *StdIoRealloc(InputStream *, void *mem, size_t size) { - return realloc(mem, size); -} - -void StdIoFree(InputStream *, void *mem) { - free(mem); -} - -int StdIoProgress(InputStream *, ulonglong cur, ulonglong max) { - return 1; -} - -longlong StdIoGetFileSize(InputStream *_st) { - MkvStdIO *st = (MkvStdIO *) _st; - longlong epos = 0; - longlong cpos = std_ftell(st->fp); - std_fseek(st->fp, 0, SEEK_END); - epos = std_ftell(st->fp); - std_fseek(st->fp, cpos, SEEK_SET); +longlong StdIoGetFileSize(InputStream *st) { + auto fp = static_cast(st)->fp; + auto cpos = std_ftell(fp); + std_fseek(fp, 0, SEEK_END); + auto epos = std_ftell(fp); + std_fseek(fp, cpos, SEEK_SET); return epos; } -MkvStdIO::MkvStdIO(wxString filename) +MkvStdIO::MkvStdIO(agi::fs::path const& filename) : error(0) { read = StdIoRead; scan = StdIoScan; - getcachesize = StdIoGetCacheSize; - geterror = StdIoGetLastError; - memalloc = StdIoMalloc; - memrealloc = StdIoRealloc; - memfree = StdIoFree; - progress = StdIoProgress; + getcachesize = [](InputStream *) -> unsigned int { return CACHESIZE; }; + geterror = [](InputStream *st) -> const char * { return strerror(((MkvStdIO *)st)->error); }; + memalloc = [](InputStream *, size_t size) { return malloc(size); }; + memrealloc = [](InputStream *, void *mem, size_t size) { return realloc(mem, size); }; + memfree = [](InputStream *, void *mem) { free(mem); }; + progress = [](InputStream *, ulonglong, ulonglong) { return 1; }; getfilesize = StdIoGetFileSize; - wxFileName fname(filename); #ifdef __VISUALC__ - fp = _wfopen(fname.GetFullPath().wc_str(), L"rb"); + fp = _wfopen(filename.c_str(), L"rb"); #else - fp = fopen(fname.GetFullPath().utf8_str(), "rb"); + fp = fopen(filename.c_str(), "rb"); #endif - if (fp) { - setvbuf(fp, nullptr, _IOFBF, CACHESIZE); - } - else { - throw agi::FileNotFoundError(from_wx(filename)); - } + if (!fp) + throw agi::fs::FileNotFound(filename); + + setvbuf(fp, nullptr, _IOFBF, CACHESIZE); } diff --git a/aegisub/src/mkv_wrap.h b/aegisub/src/mkv_wrap.h index 83c13f8bc..e7a3406f1 100644 --- a/aegisub/src/mkv_wrap.h +++ b/aegisub/src/mkv_wrap.h @@ -1,29 +1,16 @@ -// Copyright (c) 2006, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2013, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. +// 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/ @@ -32,10 +19,8 @@ /// @ingroup video_input /// -#include -#include - #include +#include DEFINE_SIMPLE_EXCEPTION_NOINNER(MatroskaException, agi::Exception, "matroksa_wrapper/generic") @@ -44,7 +29,7 @@ class AssFile; class MatroskaWrapper { public: /// Check if the file is a matroska file with at least one subtitle track - static bool HasSubtitles(wxString const& filename); + static bool HasSubtitles(agi::fs::path const& filename); /// Load subtitles from a matroska file - static void GetSubtitles(wxString const& filename, AssFile *target); + static void GetSubtitles(agi::fs::path const& filename, AssFile *target); }; diff --git a/aegisub/src/options.h b/aegisub/src/options.h index 6c3261fc5..bded9e415 100644 --- a/aegisub/src/options.h +++ b/aegisub/src/options.h @@ -18,10 +18,15 @@ #include #include +namespace agi { + class Path; +} + /// For holding all configuration-related objects and values. namespace config { extern agi::Options *opt; ///< Options extern agi::MRUManager *mru; ///< Most Recently Used + extern agi::Path *path; } /// Macro to get OptionValue object diff --git a/aegisub/src/preferences.cpp b/aegisub/src/preferences.cpp index c6c3eed93..1727c3ed9 100644 --- a/aegisub/src/preferences.cpp +++ b/aegisub/src/preferences.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include diff --git a/aegisub/src/preferences_base.cpp b/aegisub/src/preferences_base.cpp index f85569894..f51c5034e 100644 --- a/aegisub/src/preferences_base.cpp +++ b/aegisub/src/preferences_base.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -64,8 +63,7 @@ OPTION_UPDATER(BoolUpdater, wxCommandEvent, OptionValueBool, !!evt.GetInt()); OPTION_UPDATER(ColourUpdater, wxThreadEvent, OptionValueColor, evt.GetPayload()); static void browse_button(wxTextCtrl *ctrl) { - wxString def = StandardPaths::DecodePath(ctrl->GetValue()); - wxDirDialog dlg(0, _("Please choose the folder:"), def); + wxDirDialog dlg(0, _("Please choose the folder:"), StandardPaths::DecodePath(from_wx(ctrl->GetValue())).wstring()); if (dlg.ShowModal() == wxID_OK) { wxString dir = dlg.GetPath(); if (!dir.empty()) diff --git a/aegisub/src/preferences_base.h b/aegisub/src/preferences_base.h index dce5b48b4..810afffff 100644 --- a/aegisub/src/preferences_base.h +++ b/aegisub/src/preferences_base.h @@ -17,7 +17,15 @@ /// @see preferences_base.cpp /// @ingroup configuration_ui +#include +#include + class Preferences; +class wxControl; +class wxFlexGridSizer; +class wxSizer; +class wxString; +class wxTreebook; class OptionPage : public wxScrolled { template @@ -32,7 +40,6 @@ protected: wxSizer *sizer; Preferences *parent; - wxFlexGridSizer* PageSizer(wxString name); void CellSkip(wxFlexGridSizer *flex); diff --git a/aegisub/src/scintilla_text_selection_controller.cpp b/aegisub/src/scintilla_text_selection_controller.cpp index b5c679503..642b05bda 100644 --- a/aegisub/src/scintilla_text_selection_controller.cpp +++ b/aegisub/src/scintilla_text_selection_controller.cpp @@ -26,21 +26,21 @@ ScintillaTextSelectionController::ScintillaTextSelectionController(ScintillaText } void ScintillaTextSelectionController::SetInsertionPoint(int position) { - ctrl->SetInsertionPoint(ctrl->GetUnicodePosition(position)); + ctrl->SetInsertionPoint(position); } int ScintillaTextSelectionController::GetInsertionPoint() const { - return ctrl->GetReverseUnicodePosition(ctrl->GetInsertionPoint()); + return ctrl->GetInsertionPoint(); } void ScintillaTextSelectionController::SetSelection(int start, int end) { - ctrl->SetSelectionU(start, end); + ctrl->SetSelection(start, end); } int ScintillaTextSelectionController::GetSelectionStart() const { - return ctrl->GetReverseUnicodePosition(ctrl->GetSelectionStart()); + return ctrl->GetSelectionStart(); } int ScintillaTextSelectionController::GetSelectionEnd() const { - return ctrl->GetReverseUnicodePosition(ctrl->GetSelectionEnd()); + return ctrl->GetSelectionEnd(); } diff --git a/aegisub/src/search_replace_engine.cpp b/aegisub/src/search_replace_engine.cpp index ccb999ee4..6fb45d60a 100644 --- a/aegisub/src/search_replace_engine.cpp +++ b/aegisub/src/search_replace_engine.cpp @@ -26,14 +26,13 @@ #include +#include + #include -#include static const size_t bad_pos = -1; namespace { -DEFINE_SIMPLE_EXCEPTION(BadRegex, agi::InvalidInputException, "bad_regex") - auto get_dialogue_field(SearchReplaceSettings::Field field) -> decltype(&AssDialogue::Text) { switch (field) { case SearchReplaceSettings::Field::TEXT: return &AssDialogue::Text; @@ -47,28 +46,28 @@ auto get_dialogue_field(SearchReplaceSettings::Field field) -> decltype(&AssDial typedef std::function matcher; class noop_accessor { - boost::flyweight AssDialogue::*field; + boost::flyweight AssDialogue::*field; size_t start; public: noop_accessor(SearchReplaceSettings::Field f) : field(get_dialogue_field(f)), start(0) { } - wxString get(const AssDialogue *d, size_t s) { + std::string get(const AssDialogue *d, size_t s) { start = s; return (d->*field).get().substr(s); } - MatchState make_match_state(size_t s, size_t e, wxRegEx *r = nullptr) { + MatchState make_match_state(size_t s, size_t e, boost::regex *r = nullptr) { return MatchState(s + start, e + start, r); } }; class skip_tags_accessor { - boost::flyweight AssDialogue::*field; + boost::flyweight AssDialogue::*field; std::vector> blocks; size_t start; - void parse_str(wxString const& str) { + void parse_str(std::string const& str) { blocks.clear(); size_t ovr_start = bad_pos; @@ -87,11 +86,11 @@ class skip_tags_accessor { public: skip_tags_accessor(SearchReplaceSettings::Field f) : field(get_dialogue_field(f)), start(0) { } - wxString get(const AssDialogue *d, size_t s) { + std::string get(const AssDialogue *d, size_t s) { auto const& str = (d->*field).get(); parse_str(str); - wxString out; + std::string out; size_t last = s; for (auto const& block : blocks) { @@ -108,7 +107,7 @@ public: return out; } - MatchState make_match_state(size_t s, size_t e, wxRegEx *r = nullptr) { + MatchState make_match_state(size_t s, size_t e, boost::regex *r = nullptr) { s += start; e += start; @@ -140,31 +139,28 @@ public: }; template -matcher get_matcher(SearchReplaceSettings const& settings, wxRegEx *regex, Accessor&& a) { +matcher get_matcher(SearchReplaceSettings const& settings, Accessor&& a) { if (settings.use_regex) { - int flags = wxRE_ADVANCED; + int flags = boost::regex::perl; if (!settings.match_case) - flags |= wxRE_ICASE; + flags |= boost::regex::icase; - regex->Compile(settings.find, flags); - if (!regex->IsValid()) - throw BadRegex("Invalid syntax in regular expression", nullptr); + boost::regex regex(settings.find, flags); return [=](const AssDialogue *diag, size_t start) mutable -> MatchState { - if (!regex->Matches(a.get(diag, start))) + boost::smatch result; + auto const& str = a.get(diag, start); + if (!regex_search(str, result, regex, start > 0 ? boost::match_not_bol : boost::match_default)) return MatchState(); - - size_t match_start, match_len; - regex->GetMatch(&match_start, &match_len, 0); - return a.make_match_state(match_start, match_start + match_len, regex); + return a.make_match_state(result.position(), result.position() + result.length(), ®ex); }; } bool full_match_only = settings.exact_match; bool match_case = settings.match_case; - wxString look_for = settings.find; + std::string look_for = settings.find; if (!settings.match_case) - look_for.MakeLower(); + boost::to_lower(look_for); return [=](const AssDialogue *diag, size_t start) mutable -> MatchState { auto str = a.get(diag, start); @@ -172,10 +168,10 @@ matcher get_matcher(SearchReplaceSettings const& settings, wxRegEx *regex, Acces return MatchState(); if (!match_case) - str.MakeLower(); + boost::to_lower(str); size_t pos = str.find(look_for); - if (pos == wxString::npos) + if (pos == std::string::npos) return MatchState(); return a.make_match_state(pos, pos + look_for.size()); @@ -192,10 +188,10 @@ Iterator circular_next(Iterator it, Container& c) { } -std::function SearchReplaceEngine::GetMatcher(SearchReplaceSettings const& settings, wxRegEx *regex) { +std::function SearchReplaceEngine::GetMatcher(SearchReplaceSettings const& settings) { if (settings.skip_tags) - return get_matcher(settings, regex, skip_tags_accessor(settings.field)); - return get_matcher(settings, regex, noop_accessor(settings.field)); + return get_matcher(settings, skip_tags_accessor(settings.field)); + return get_matcher(settings, noop_accessor(settings.field)); } SearchReplaceEngine::SearchReplaceEngine(agi::Context *c) @@ -208,11 +204,10 @@ void SearchReplaceEngine::Replace(AssDialogue *diag, MatchState &ms) { auto& diag_field = diag->*get_dialogue_field(settings.field); auto text = diag_field.get(); - wxString replacement = settings.replace_with; + std::string replacement = settings.replace_with; if (ms.re) { - wxString to_replace = text.substr(ms.start, ms.end - ms.start); - ms.re->ReplaceFirst(&to_replace, settings.replace_with); - replacement = to_replace; + auto to_replace = text.substr(ms.start, ms.end - ms.start); + replacement = regex_replace(to_replace, *ms.re, replacement, boost::format_first_only); } diag_field = text.substr(0, ms.start) + replacement + text.substr(ms.end); @@ -223,8 +218,7 @@ bool SearchReplaceEngine::FindReplace(bool replace) { if (!initialized) return false; - wxRegEx r; - auto matches = GetMatcher(settings, &r); + auto matches = GetMatcher(settings); AssDialogue *line = context->selectionController->GetActiveLine(); auto it = context->ass->Line.iterator_to(*line); @@ -302,8 +296,7 @@ bool SearchReplaceEngine::ReplaceAll() { size_t count = 0; - wxRegEx r; - auto matches = GetMatcher(settings, &r); + auto matches = GetMatcher(settings); SubtitleSelection const& sel = context->selectionController->GetSelectedSet(); bool selection_only = settings.limit_to == SearchReplaceSettings::Limit::SELECTED; @@ -315,12 +308,9 @@ bool SearchReplaceEngine::ReplaceAll() { if (settings.use_regex) { if (MatchState ms = matches(diag, 0)) { auto& diag_field = diag->*get_dialogue_field(settings.field); - auto text = diag_field.get(); - // A zero length match (such as '$') will always be replaced - // maxMatches times, which is almost certainly not what the user - // wanted, so limit it to one replacement in that situation - count += ms.re->Replace(&text, settings.replace_with, ms.start == ms.end); - diag_field = text; + std::string const& text = diag_field.get(); + count += distance(boost::sregex_iterator(begin(text), end(text), *ms.re), boost::sregex_iterator()); + diag_field = regex_replace(text, *ms.re, settings.replace_with); } continue; } diff --git a/aegisub/src/search_replace_engine.h b/aegisub/src/search_replace_engine.h index 95c9ad1c3..5ea41d12d 100644 --- a/aegisub/src/search_replace_engine.h +++ b/aegisub/src/search_replace_engine.h @@ -15,18 +15,18 @@ // Aegisub Project http://www.aegisub.org/ #include -#include +#include +#include namespace agi { struct Context; } class AssDialogue; -class wxRegEx; struct MatchState { - wxRegEx *re; + boost::regex *re; size_t start, end; MatchState() : re(nullptr), start(0), end(-1) { } - MatchState(size_t s, size_t e, wxRegEx *re) : re(re), start(s), end(e) { } + MatchState(size_t s, size_t e, boost::regex *re) : re(re), start(s), end(e) { } operator bool() const { return end != (size_t)-1; } }; @@ -43,8 +43,8 @@ struct SearchReplaceSettings { SELECTED }; - wxString find; - wxString replace_with; + std::string find; + std::string replace_with; Field field; Limit limit_to; @@ -71,7 +71,7 @@ public: void Configure(SearchReplaceSettings const& new_settings); - static std::function GetMatcher(SearchReplaceSettings const& settings, wxRegEx *regex); + static std::function GetMatcher(SearchReplaceSettings const& settings); SearchReplaceEngine(agi::Context *c); }; diff --git a/aegisub/src/spellchecker.cpp b/aegisub/src/spellchecker.cpp index 01073f730..f070fee4a 100644 --- a/aegisub/src/spellchecker.cpp +++ b/aegisub/src/spellchecker.cpp @@ -34,27 +34,21 @@ #include "config.h" -#ifdef WITH_HUNSPELL -#include "spellchecker_hunspell.h" -#endif - -#include "compat.h" #include "include/aegisub/spellchecker.h" +#include "spellchecker_hunspell.h" + #include "options.h" agi::SpellChecker *SpellCheckerFactory::GetSpellChecker() { std::vector list = GetClasses(OPT_GET("Tool/Spell Checker/Backend")->GetString()); if (list.empty()) return nullptr; - // Get provider - wxString error; + std::string error; for (auto const& name : list) { try { agi::SpellChecker *checker = Create(name); if (checker) return checker; } - catch (wxString const& err) { error += name + " factory: " + err + "\n"; } - catch (const char *err) { error += name + " factory: " + wxString(err) + "\n"; } catch (...) { error += name + " factory: Unknown error\n"; } } diff --git a/aegisub/src/spellchecker_hunspell.cpp b/aegisub/src/spellchecker_hunspell.cpp index fd71c0359..7062fae8f 100644 --- a/aegisub/src/spellchecker_hunspell.cpp +++ b/aegisub/src/spellchecker_hunspell.cpp @@ -21,26 +21,20 @@ #include "config.h" #ifdef WITH_HUNSPELL - #include "spellchecker_hunspell.h" -#include -#include -#include - -#include -#include - -#include +#include "options.h" +#include "standard_paths.h" +#include +#include #include #include #include -#include "charset_conv.h" -#include "compat.h" -#include "options.h" -#include "standard_paths.h" +#include + +#include HunspellSpellChecker::HunspellSpellChecker() : lang_listener(OPT_SUB("Tool/Spell Checker/Language", &HunspellSpellChecker::OnLanguageChanged, this)) @@ -97,26 +91,24 @@ void HunspellSpellChecker::ReadUserDictionary() { // Read the old contents of the user's dictionary try { - agi::scoped_ptr stream(agi::io::Open(from_wx(userDicPath))); + std::unique_ptr stream(agi::io::Open(userDicPath)); copy_if( ++agi::line_iterator(*stream), agi::line_iterator(), inserter(customWords, customWords.end()), [](std::string const& str) { return !str.empty(); }); } - catch (agi::FileNotFoundError const&) { + catch (agi::fs::FileNotFound const&) { // Not an error; user dictionary just doesn't exist } } void HunspellSpellChecker::WriteUserDictionary() { // Ensure that the path exists - wxFileName fn(userDicPath); - if (!fn.DirExists()) - wxFileName::Mkdir(fn.GetPath()); + agi::fs::CreateDirectory(userDicPath.parent_path()); // Write the new dictionary { - agi::io::Save writer(from_wx(userDicPath)); + agi::io::Save writer(userDicPath); writer.Get() << customWords.size() << "\n"; copy(customWords.begin(), customWords.end(), std::ostream_iterator(writer.Get(), "\n")); } @@ -165,23 +157,21 @@ std::vector HunspellSpellChecker::GetSuggestions(std::string const& std::vector HunspellSpellChecker::GetLanguageList() { if (!languages.empty()) return languages; - wxArrayString dic, aff; + std::vector dic, aff; // Get list of dictionaries - wxString path = StandardPaths::DecodePath("?data/dictionaries/"); - if (wxFileName::DirExists(path)) { - wxDir::GetAllFiles(path, &dic, "*.dic", wxDIR_FILES); - wxDir::GetAllFiles(path, &aff, "*.aff", wxDIR_FILES); - } - path = StandardPaths::DecodePath(to_wx(OPT_GET("Path/Dictionary")->GetString()) + "/"); - if (wxFileName::DirExists(path)) { - wxDir::GetAllFiles(path, &dic, "*.dic", wxDIR_FILES); - wxDir::GetAllFiles(path, &aff, "*.aff", wxDIR_FILES); - } - if (aff.empty()) return std::vector(); + auto path = StandardPaths::DecodePath("?data/dictionaries/"); + agi::fs::DirectoryIterator(path, "*.dic").GetAll(dic); + agi::fs::DirectoryIterator(path, "*.aff").GetAll(aff); - dic.Sort(); - aff.Sort(); + path = StandardPaths::DecodePath(OPT_GET("Path/Dictionary")->GetString()); + agi::fs::DirectoryIterator(path, "*.dic").GetAll(dic); + agi::fs::DirectoryIterator(path, "*.aff").GetAll(aff); + + if (dic.empty() || aff.empty()) return languages; + + sort(begin(dic), end(dic)); + sort(begin(aff), end(aff)); // Drop extensions for (size_t i = 0; i < dic.size(); ++i) dic[i].resize(dic[i].size() - 4); @@ -189,15 +179,14 @@ std::vector HunspellSpellChecker::GetLanguageList() { // Verify that each aff has a dic for (size_t i = 0, j = 0; i < dic.size() && j < aff.size(); ) { - int cmp = dic[i].Cmp(aff[j]); + int cmp = dic[i].compare(aff[j]); if (cmp < 0) ++i; else if (cmp > 0) ++j; else { // Don't insert a language twice if it's in both the user dir and // the app's dir - std::string name = from_wx(wxFileName(aff[j]).GetName()); - if (languages.empty() || name != languages.back()) - languages.push_back(name); + if (languages.empty() || aff[j] != languages.back()) + languages.push_back(aff[j]); ++i; ++j; } @@ -208,31 +197,31 @@ std::vector HunspellSpellChecker::GetLanguageList() { void HunspellSpellChecker::OnLanguageChanged() { hunspell.reset(); - std::string language = OPT_GET("Tool/Spell Checker/Language")->GetString(); + auto language = OPT_GET("Tool/Spell Checker/Language")->GetString(); if (language.empty()) return; - wxString custDicRoot = StandardPaths::DecodePath(to_wx(OPT_GET("Path/Dictionary")->GetString())); - wxString dataDicRoot = StandardPaths::DecodePath("?data/dictionaries"); + auto custDicRoot = StandardPaths::DecodePath(OPT_GET("Path/Dictionary")->GetString()); + auto dataDicRoot = StandardPaths::DecodePath("?data/dictionaries"); // If the user has a dic/aff pair in their dictionary path for this language // use that; otherwise use the one from Aegisub's install dir, adding words // from the dic in the user's dictionary path if it exists - wxString affPath = wxString::Format("%s/%s.aff", custDicRoot, language); - wxString dicPath = wxString::Format("%s/%s.dic", custDicRoot, language); - userDicPath = wxString::Format("%s/user_%s.dic", StandardPaths::DecodePath("?user/dictionaries"), language); - if (!wxFileExists(affPath) || !wxFileExists(dicPath)) { - affPath = wxString::Format("%s/%s.aff", dataDicRoot, language); - dicPath = wxString::Format("%s/%s.dic", dataDicRoot, language); + auto affPath = custDicRoot/(language + ".aff"); + auto dicPath = custDicRoot/(language + ".dic"); + userDicPath = StandardPaths::DecodePath("?user/dictionaries")/str(boost::format("user_%s.dic") % language); + if (!agi::fs::FileExists(affPath) || !agi::fs::FileExists(dicPath)) { + affPath = dataDicRoot/(language + ".aff"); + dicPath = dataDicRoot/(language + ".dic"); } LOG_I("dictionary/file") << dicPath; - if (!wxFileExists(affPath) || !wxFileExists(dicPath)) { + if (!agi::fs::FileExists(affPath) || !agi::fs::FileExists(dicPath)) { LOG_D("dictionary/file") << "Dictionary not found"; return; } - hunspell.reset(new Hunspell(affPath.mb_str(csConvLocal), dicPath.mb_str(csConvLocal))); + hunspell.reset(new Hunspell(agi::fs::ShortName(affPath).c_str(), agi::fs::ShortName(dicPath).c_str())); if (!hunspell) return; conv.reset(new agi::charset::IconvWrapper("utf-8", hunspell->get_dic_encoding())); diff --git a/aegisub/src/spellchecker_hunspell.h b/aegisub/src/spellchecker_hunspell.h index 1a96b0266..2f64e6fcd 100644 --- a/aegisub/src/spellchecker_hunspell.h +++ b/aegisub/src/spellchecker_hunspell.h @@ -20,33 +20,32 @@ /// #ifdef WITH_HUNSPELL - #include -#include +#include #include +#include +#include #include -#include - namespace agi { namespace charset { class IconvWrapper; } } class Hunspell; /// @brief Hunspell-based spell checker implementation class HunspellSpellChecker : public agi::SpellChecker { /// Hunspell instance - agi::scoped_ptr hunspell; + std::unique_ptr hunspell; /// Conversions between the dictionary charset and utf-8 - agi::scoped_ptr conv; - agi::scoped_ptr rconv; + std::unique_ptr conv; + std::unique_ptr rconv; /// Languages which we have dictionaries for std::vector languages; /// Path to user-local dictionary. - wxString userDicPath; + agi::fs::path userDicPath; /// Words in the custom user dictionary std::set customWords; diff --git a/aegisub/src/spline.cpp b/aegisub/src/spline.cpp index 57dc120ea..b2a35bcae 100644 --- a/aegisub/src/spline.cpp +++ b/aegisub/src/spline.cpp @@ -34,15 +34,16 @@ #include "config.h" -#include - -#include - #include "spline.h" #include "utils.h" #include "visual_tool.h" +#include + +#include +#include + Spline::Spline(const VisualToolBase &tl) : coord_translator(tl) , scale(1) @@ -63,8 +64,8 @@ void Spline::SetScale(int new_scale) { scale = 1 << (raw_scale - 1); } -wxString Spline::EncodeToAss() const { - wxString result; +std::string Spline::EncodeToAss() const { + std::string result; result.reserve(size() * 10); char last = 0; @@ -103,7 +104,7 @@ wxString Spline::EncodeToAss() const { return result; } -void Spline::DecodeFromAss(wxString str) { +void Spline::DecodeFromAss(std::string const& str) { // Clear current clear(); std::vector stack; @@ -113,11 +114,10 @@ void Spline::DecodeFromAss(wxString str) { Vector2D pt(0, 0); // Tokenize the string - wxStringTokenizer tkn(str, " "); - while (tkn.HasMoreTokens()) { - wxString token = tkn.GetNextToken(); + boost::char_separator sep("|"); + for (auto const& token : boost::tokenizer>(str, sep)) { double n; - if (token.ToCDouble(&n)) { + if (agi::util::try_parse(token, &n)) { stack.push_back(n); // Move @@ -149,7 +149,6 @@ void Spline::DecodeFromAss(wxString str) { stack.clear(); } } - // Got something else else if (token.size() == 1) { command = token[0]; diff --git a/aegisub/src/spline.h b/aegisub/src/spline.h index d6ac8e17a..6024782da 100644 --- a/aegisub/src/spline.h +++ b/aegisub/src/spline.h @@ -33,9 +33,7 @@ /// #include -#include - -#include +#include #include "spline_curve.h" @@ -57,10 +55,10 @@ public: Spline(const VisualToolBase &scale); /// Encode to an ASS vector drawing - wxString EncodeToAss() const; + std::string EncodeToAss() const; /// Decode an ASS vector drawing - void DecodeFromAss(wxString str); + void DecodeFromAss(std::string const& str); /// Set the scale /// @param new_scale Power-of-two to scale coordinates by diff --git a/aegisub/src/standard_paths.cpp b/aegisub/src/standard_paths.cpp index 9be84b380..d34fc8990 100644 --- a/aegisub/src/standard_paths.cpp +++ b/aegisub/src/standard_paths.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2013, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. +// 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/ @@ -36,60 +23,14 @@ #include "standard_paths.h" -#include -#include +#include "options.h" -StandardPaths &StandardPaths::GetInstance() { - static StandardPaths instance; - return instance; +#include + +agi::fs::path StandardPaths::DecodePath(std::string const& path) { + return config::path->Decode(path); } -StandardPaths::StandardPaths() { - wxStandardPathsBase &paths = wxStandardPaths::Get(); - -#if defined(__UNIX__) && !defined(__APPLE__) - // Relocation support, this is required to set the prefix to all - // wx StandardPaths. - static_cast(paths).SetInstallPrefix(wxT(INSTALL_PREFIX)); -#endif - - DoSetPathValue("?data", paths.GetDataDir()); - DoSetPathValue("?user", paths.GetUserDataDir()); - DoSetPathValue("?local", paths.GetUserLocalDataDir()); - DoSetPathValue("?temp", paths.GetTempDir()); - - // Create paths if they don't exist - if (!wxDirExists(paths.GetUserDataDir())) - wxMkDir(paths.GetUserDataDir(), 0777); - if (!wxDirExists(paths.GetUserLocalDataDir())) - wxMkDir(paths.GetUserLocalDataDir(), 0777); -} - -wxString StandardPaths::DoDecodePath(wxString path) { - if (!path || path[0] != '?') - return path; - - // Split ?part from rest - path.Replace("\\","/"); - int pos = path.Find("/"); - wxString path1,path2; - if (pos == wxNOT_FOUND) path1 = path; - else { - path1 = path.Left(pos); - path2 = path.Mid(pos+1); - } - - // Replace ?part if valid - auto iter = paths.find(path1); - if (iter == paths.end()) return path; - wxString final = iter->second + "/" + path2; - final.Replace("//","/"); -#ifdef WIN32 - final.Replace("/","\\"); -#endif - return final; -} - -void StandardPaths::DoSetPathValue(const wxString &path, const wxString &value) { - paths[path] = value; +void StandardPaths::SetPathValue(std::string const& path, agi::fs::path const& value) { + config::path->SetToken(path, value); } diff --git a/aegisub/src/standard_paths.h b/aegisub/src/standard_paths.h index 52ddb0981..2f8f2c93d 100644 --- a/aegisub/src/standard_paths.h +++ b/aegisub/src/standard_paths.h @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2013, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. +// 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/ @@ -32,22 +19,12 @@ /// @ingroup utility /// -#include -#include +#include + +#include class StandardPaths { - static StandardPaths &GetInstance(); - - std::map paths; - - StandardPaths(); - StandardPaths(StandardPaths const&); - StandardPaths& operator=(StandardPaths const&); - - wxString DoDecodePath(wxString path); - void DoSetPathValue(const wxString &path, const wxString &value); - public: - static wxString DecodePath(const wxString &path) { return GetInstance().DoDecodePath(path); } - static void SetPathValue(const wxString &path, const wxString &value) { GetInstance().DoSetPathValue(path,value); } + static agi::fs::path DecodePath(std::string const& path); + static void SetPathValue(std::string const& path, agi::fs::path const& value); }; diff --git a/aegisub/src/string_codec.cpp b/aegisub/src/string_codec.cpp index f53920635..7aeb585ef 100644 --- a/aegisub/src/string_codec.cpp +++ b/aegisub/src/string_codec.cpp @@ -39,41 +39,30 @@ #include "string_codec.h" -wxString inline_string_encode(const wxString &input) -{ - const size_t inlen = input.length(); - wxString output(""); - output.Alloc(inlen); - for (size_t i = 0; i < inlen; i++) { - wxChar c = input[i]; - if (c <= 0x1F || c == 0x23 || c == 0x2C || c == 0x3A || c == 0x7C) { - output << wxString::Format("#%02X", c); - } else { - output << c; - } +#include + +std::string inline_string_encode(const std::string &input) { + std::string output; + output.reserve(input.size()); + auto format = boost::format("#%02X"); + for (char c : input) { + if (c <= 0x1F || c == 0x23 || c == 0x2C || c == 0x3A || c == 0x7C) + output += str(format % c); + else + output += c; } return output; } -wxString inline_string_decode(const wxString &input) -{ - const size_t inlen = input.length(); - wxString output(""); - output.Alloc(inlen); - size_t i = 0; - while (i < inlen) { - if (input[i] == '#') { - // check if there's actually enough extra characters at the end of the string - if (inlen - i < 3) - break; - wxString charcode = input.Mid(i+1, 2); - long c; - if (charcode.ToLong(&c, 16)) { - output << (wchar_t)c; - } - i += 3; - } else { - output << input[i++]; +std::string inline_string_decode(const std::string &input) { + std::string output; + output.reserve(input.size()); + for (size_t i = 0; i < input.size() - 2; ++i) { + if (input[i] != '#') + output += input[i]; + else { + output += (char)strtol(input.substr(i + 1, 2).c_str(), nullptr, 16); + i += 2; } } return output; diff --git a/aegisub/src/string_codec.h b/aegisub/src/string_codec.h index 498d314b6..f638a580d 100644 --- a/aegisub/src/string_codec.h +++ b/aegisub/src/string_codec.h @@ -52,7 +52,7 @@ /// /// The encoded string should be usable in any kind of field in an ASS file. -#include +#include -wxString inline_string_encode(const wxString &input); -wxString inline_string_decode(const wxString &input); +std::string inline_string_encode(const std::string &input); +std::string inline_string_decode(const std::string &input); diff --git a/aegisub/src/subs_edit_box.cpp b/aegisub/src/subs_edit_box.cpp index 4cea88c7d..e5e250e3d 100644 --- a/aegisub/src/subs_edit_box.cpp +++ b/aegisub/src/subs_edit_box.cpp @@ -34,19 +34,6 @@ #include "config.h" -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "subs_edit_box.h" #include "ass_dialogue.h" @@ -70,6 +57,19 @@ #include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + namespace { /// Work around wxGTK's fondness for generating events from ChangeValue @@ -92,7 +92,7 @@ void time_edit_char_hook(wxKeyEvent &event) { SubsEditBox::SubsEditBox(wxWindow *parent, agi::Context *context) : wxPanel(parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxRAISED_BORDER, "SubsEditBox") -, line(0) +, line(nullptr) , button_bar_split(true) , controls_enabled(true) , c(context) @@ -232,9 +232,9 @@ wxTextCtrl *SubsEditBox::MakeMarginCtrl(wxString const& tooltip, int margin, wxS middle_left_sizer->Add(ctrl, wxSizerFlags().Center()); Bind(wxEVT_COMMAND_TEXT_UPDATED, [=](wxCommandEvent&) { - wxString value = ctrl->GetValue(); - SetSelectedRows([&](AssDialogue *d) { d->SetMarginString(value, margin); }, commit_msg, AssFile::COMMIT_DIAG_META); - if (line) change_value(ctrl, line->GetMarginString(margin)); + int value = mid(0, boost::lexical_cast(from_wx(ctrl->GetValue())), 9999); + SetSelectedRows([&](AssDialogue *d) { d->Margin[margin] = value; }, commit_msg, AssFile::COMMIT_DIAG_META); + if (line) change_value(ctrl, to_wx(line->GetMarginString(margin))); }, ctrl->GetId()); return ctrl; @@ -305,7 +305,7 @@ void SubsEditBox::OnCommit(int type) { return; } else if (type & AssFile::COMMIT_STYLES) - style_box->Select(style_box->FindString(line->Style)); + style_box->Select(style_box->FindString(to_wx(line->Style))); if (!(type ^ AssFile::COMMIT_ORDER)) return; @@ -319,38 +319,39 @@ void SubsEditBox::OnCommit(int type) { } if (type & AssFile::COMMIT_DIAG_TEXT) { - edit_ctrl->SetTextTo(line->Text); + edit_ctrl->SetTextTo(to_wx(line->Text)); UpdateCharacterCount(line->Text); } if (type & AssFile::COMMIT_DIAG_META) { layer->SetValue(line->Layer); for (size_t i = 0; i < margin.size(); ++i) - change_value(margin[i], line->GetMarginString(i)); + change_value(margin[i], to_wx(line->GetMarginString(i))); comment_box->SetValue(line->Comment); - style_box->Select(style_box->FindString(line->Style)); + style_box->Select(style_box->FindString(to_wx(line->Style))); PopulateList(effect_box, &AssDialogue::Effect); - effect_box->ChangeValue(line->Effect); - effect_box->SetStringSelection(line->Effect); + effect_box->ChangeValue(to_wx(line->Effect)); + effect_box->SetStringSelection(to_wx(line->Effect)); PopulateList(actor_box, &AssDialogue::Actor); - actor_box->ChangeValue(line->Actor); - actor_box->SetStringSelection(line->Actor); - edit_ctrl->SetTextTo(line->Text); + actor_box->ChangeValue(to_wx(line->Actor)); + actor_box->SetStringSelection(to_wx(line->Actor)); + edit_ctrl->SetTextTo(to_wx(line->Text)); } } -void SubsEditBox::PopulateList(wxComboBox *combo, boost::flyweight AssDialogue::*field) { +void SubsEditBox::PopulateList(wxComboBox *combo, boost::flyweight AssDialogue::*field) { wxEventBlocker blocker(this); - std::unordered_set values; + std::unordered_set values; for (auto diag : c->ass->Line | agi::of_type()) values.insert(diag->*field); values.erase(""); wxArrayString arrstr; arrstr.reserve(values.size()); - copy(values.begin(), values.end(), std::back_inserter(arrstr)); + transform(values.begin(), values.end(), std::back_inserter(arrstr), + (wxString (*)(std::string const&))to_wx); combo->Freeze(); long pos = combo->GetInsertionPoint(); @@ -384,9 +385,9 @@ void SubsEditBox::OnSelectedSetChanged(const SubtitleSelection &, const Subtitle initial_times.clear(); } -void SubsEditBox::OnLineInitialTextChanged(wxString const& new_text) { +void SubsEditBox::OnLineInitialTextChanged(std::string const& new_text) { if (split_box->IsChecked()) - secondary_editor->SetValue(new_text); + secondary_editor->SetValue(to_wx(new_text)); } void SubsEditBox::UpdateFrameTiming(agi::vfr::Framerate const& fps) { @@ -408,7 +409,7 @@ void SubsEditBox::OnKeyDown(wxKeyEvent &event) { } void SubsEditBox::OnChange(wxStyledTextEvent &event) { - if (line && edit_ctrl->GetText() != line->Text) { + if (line && edit_ctrl->GetTextRaw().data() != line->Text.get()) { if (event.GetModificationType() & wxSTC_STARTACTION) commit_id = -1; CommitText(_("modify text")); @@ -434,8 +435,15 @@ void SubsEditBox::SetSelectedRows(T AssDialogue::*field, T value, wxString const SetSelectedRows([&](AssDialogue *d) { d->*field = value; }, desc, type, amend); } +template +void SubsEditBox::SetSelectedRows(T AssDialogue::*field, wxString const& value, wxString const& desc, int type, bool amend) { + boost::flyweight conv_value(from_wx(value)); + SetSelectedRows([&](AssDialogue *d) { d->*field = conv_value; }, desc, type, amend); +} + void SubsEditBox::CommitText(wxString const& desc) { - SetSelectedRows(&AssDialogue::Text, boost::flyweight(edit_ctrl->GetText()), desc, AssFile::COMMIT_DIAG_TEXT, true); + auto data = edit_ctrl->GetTextRaw(); + SetSelectedRows(&AssDialogue::Text, boost::flyweight(data.data(), data.length()), desc, AssFile::COMMIT_DIAG_TEXT, true); } void SubsEditBox::CommitTimes(TimeField field) { @@ -534,16 +542,16 @@ void SubsEditBox::OnSplit(wxCommandEvent&) { Thaw(); if (split_box->IsChecked()) - secondary_editor->SetValue(c->initialLineState->GetInitialText()); + secondary_editor->SetValue(to_wx(c->initialLineState->GetInitialText())); } void SubsEditBox::OnStyleChange(wxCommandEvent &) { - SetSelectedRows(&AssDialogue::Style, boost::flyweight(style_box->GetValue()), _("style change"), AssFile::COMMIT_DIAG_META); + SetSelectedRows(&AssDialogue::Style, style_box->GetValue(), _("style change"), AssFile::COMMIT_DIAG_META); } void SubsEditBox::OnActorChange(wxCommandEvent &evt) { bool amend = evt.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED; - SetSelectedRows(&AssDialogue::Actor, boost::flyweight(actor_box->GetValue()), _("actor change"), AssFile::COMMIT_DIAG_META, amend); + SetSelectedRows(&AssDialogue::Actor, actor_box->GetValue(), _("actor change"), AssFile::COMMIT_DIAG_META, amend); PopulateList(actor_box, &AssDialogue::Actor); } @@ -553,7 +561,7 @@ void SubsEditBox::OnLayerEnter(wxCommandEvent &) { void SubsEditBox::OnEffectChange(wxCommandEvent &evt) { bool amend = evt.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED; - SetSelectedRows(&AssDialogue::Effect, boost::flyweight(effect_box->GetValue()), _("effect change"), AssFile::COMMIT_DIAG_META, amend); + SetSelectedRows(&AssDialogue::Effect, effect_box->GetValue(), _("effect change"), AssFile::COMMIT_DIAG_META, amend); PopulateList(effect_box, &AssDialogue::Effect); } @@ -566,7 +574,7 @@ void SubsEditBox::CallCommand(const char *cmd_name) { edit_ctrl->SetFocus(); } -void SubsEditBox::UpdateCharacterCount(wxString const& text) { +void SubsEditBox::UpdateCharacterCount(std::string const& text) { size_t length = MaxLineLength(text); char_count->SetValue(wxString::Format("%lu", (unsigned long)length)); size_t limit = (size_t)OPT_GET("Subtitle/Character Limit")->GetInt(); diff --git a/aegisub/src/subs_edit_box.h b/aegisub/src/subs_edit_box.h index f6dc58715..998cc6331 100644 --- a/aegisub/src/subs_edit_box.h +++ b/aegisub/src/subs_edit_box.h @@ -38,6 +38,7 @@ #include #include +#include #include #include @@ -55,7 +56,6 @@ class TextSelectionController; class TimeEdit; class wxButton; class wxCheckBox; -class wxComboBox; class wxRadioButton; class wxSizer; class wxSpinCtrl; @@ -149,7 +149,7 @@ class SubsEditBox : public wxPanel { void OnActiveLineChanged(AssDialogue *new_line); void OnSelectedSetChanged(const SubtitleSelection &, const SubtitleSelection &); - void OnLineInitialTextChanged(wxString const& new_text); + void OnLineInitialTextChanged(std::string const& new_text); void OnFrameTimeRadio(wxCommandEvent &event); void OnStyleChange(wxCommandEvent &event); @@ -179,18 +179,21 @@ class SubsEditBox : public wxPanel { template void SetSelectedRows(T AssDialogue::*field, T value, wxString const& desc, int type, bool amend = false); + template + void SetSelectedRows(T AssDialogue::*field, wxString const& value, wxString const& desc, int type, bool amend = false); + /// @brief Reload the current line from the file /// @param type AssFile::CommitType void OnCommit(int type); /// Regenerate a dropdown list with the unique values of a dialogue field - void PopulateList(wxComboBox *combo, boost::flyweight AssDialogue::*field); + void PopulateList(wxComboBox *combo, boost::flyweight AssDialogue::*field); /// @brief Enable or disable frame timing mode void UpdateFrameTiming(agi::vfr::Framerate const& fps); /// Update the character count box for the given text - void UpdateCharacterCount(wxString const& text); + void UpdateCharacterCount(std::string const& text); /// Call a command the restore focus to the edit box void CallCommand(const char *cmd_name); diff --git a/aegisub/src/subs_edit_ctrl.cpp b/aegisub/src/subs_edit_ctrl.cpp index 3ec4c5544..7ad11e153 100644 --- a/aegisub/src/subs_edit_ctrl.cpp +++ b/aegisub/src/subs_edit_ctrl.cpp @@ -34,13 +34,6 @@ #include "config.h" -#include - -#include -#include -#include -#include - #include "subs_edit_ctrl.h" #include "ass_dialogue.h" @@ -58,6 +51,15 @@ #include #include +#include +#include +#include + +#include +#include +#include +#include + /// Event ids enum { EDIT_MENU_SPLIT_PRESERVE = 1400, @@ -226,7 +228,7 @@ void SubsTextEditCtrl::SetStyles() { void SubsTextEditCtrl::UpdateStyle() { AssDialogue *diag = context ? context->selectionController->GetActiveLine() : 0; - bool template_line = diag && diag->Comment && diag->Effect.get().Lower().StartsWith("template"); + bool template_line = diag && diag->Comment && boost::istarts_with(diag->Effect.get(), "template"); tokenized_line = agi::ass::TokenizeDialogueBody(line_text, template_line); agi::ass::SplitWords(line_text, tokenized_line); @@ -291,19 +293,19 @@ void SubsTextEditCtrl::SetTextTo(wxString const& text) { } void SubsTextEditCtrl::Paste() { - wxString data = GetClipboard(); + std::string data = GetClipboard(); - data.Replace("\r\n", "\\N"); - data.Replace("\n", "\\N"); - data.Replace("\r", "\\N"); + boost::replace_all(data, "\r\n", "\\N"); + boost::replace_all(data, "\n", "\\N"); + boost::replace_all(data, "\r", "\\N"); - int from = GetReverseUnicodePosition(GetSelectionStart()); - int to = GetReverseUnicodePosition(GetSelectionEnd()); + wxCharBuffer old = GetTextRaw(); + data.insert(0, old.data(), GetSelectionStart()); + int sel_start = data.size(); + data.append(old.data() + GetSelectionEnd()); - wxString old = GetText(); - SetText(old.Left(from) + data + old.Mid(to)); + SetTextRaw(data.c_str()); - int sel_start = GetUnicodePosition(from + data.size()); SetSelectionStart(sel_start); SetSelectionEnd(sel_start); } diff --git a/aegisub/src/subs_grid.cpp b/aegisub/src/subs_grid.cpp index f5b872543..f4fef7f33 100644 --- a/aegisub/src/subs_grid.cpp +++ b/aegisub/src/subs_grid.cpp @@ -34,34 +34,31 @@ #include "config.h" -#include -#include - -#include -#include -#include -#include - #include "subs_grid.h" -#include "include/aegisub/context.h" - #include "ass_dialogue.h" #include "ass_file.h" +#include "include/aegisub/context.h" #include "options.h" #include "utils.h" -#include "video_context.h" + +#include +#include +#include +#include SubtitlesGrid::SubtitlesGrid(wxWindow *parent, agi::Context *context) : BaseGrid(parent, context, wxDefaultSize, wxWANTS_CHARS | wxSUNKEN_BORDER) { } -static void trim_text(wxString *text) { - static wxRegEx start("^( |\t|\\\\[nNh])+"); - static wxRegEx end("( |\t|\\\\[nNh])+$"); - start.ReplaceFirst(text, ""); - end.ReplaceFirst(text, ""); +static std::string trim_text(std::string text) { + boost::regex start("^( |\t|\\\\[nNh])+"); + boost::regex end("( |\t|\\\\[nNh])+$"); + + regex_replace(text, start, "", boost::format_first_only); + regex_replace(text, end, "", boost::format_first_only); + return text; } static void expand_times(AssDialogue *src, AssDialogue *dst) { @@ -69,11 +66,9 @@ static void expand_times(AssDialogue *src, AssDialogue *dst) { dst->End = std::max(dst->End, src->End); } -static bool check_lines(AssDialogue *d1, AssDialogue *d2, bool (wxString::*pred)(wxString const&, wxString *) const) { - wxString rest; - if ((d1->Text.get().*pred)(d2->Text.get(), &rest)) { - trim_text(&rest); - d1->Text = rest; +static bool check_lines(AssDialogue *d1, AssDialogue *d2, bool (*pred)(std::string const&, std::string const&)) { + if (pred(d1->Text.get(), d2->Text.get())) { + d1->Text = trim_text(d1->Text.get().substr(d2->Text.get().size())); expand_times(d1, d2); return true; } @@ -81,14 +76,13 @@ static bool check_lines(AssDialogue *d1, AssDialogue *d2, bool (wxString::*pred) } static bool check_start(AssDialogue *d1, AssDialogue *d2) { - return check_lines(d1, d2, &wxString::StartsWith); + return check_lines(d1, d2, &boost::starts_with); } static bool check_end(AssDialogue *d1, AssDialogue *d2) { - return check_lines(d1, d2, &wxString::EndsWith); + return check_lines(d1, d2, &boost::ends_with); } -/// @brief Recombine void SubtitlesGrid::RecombineLines() { Selection selectedSet = GetSelectedSet(); if (selectedSet.size() < 2) return; @@ -97,11 +91,8 @@ void SubtitlesGrid::RecombineLines() { std::vector sel(selectedSet.begin(), selectedSet.end()); sort(sel.begin(), sel.end(), &AssFile::CompStart); - for (auto &diag : sel) { - wxString text = diag->Text; - trim_text(&text); - diag->Text = text; - } + for (auto &diag : sel) + diag->Text = trim_text(diag->Text); auto end = sel.end() - 1; for (auto cur = sel.begin(); cur != end; ++cur) { diff --git a/aegisub/src/subs_grid.h b/aegisub/src/subs_grid.h index c800fe538..93f7b3449 100644 --- a/aegisub/src/subs_grid.h +++ b/aegisub/src/subs_grid.h @@ -34,8 +34,6 @@ #include "base_grid.h" -#include - class SubtitlesGrid: public BaseGrid { public: SubtitlesGrid(wxWindow *parent, agi::Context *context); diff --git a/aegisub/src/subs_preview.cpp b/aegisub/src/subs_preview.cpp index 0f766585d..f0e6a364a 100644 --- a/aegisub/src/subs_preview.cpp +++ b/aegisub/src/subs_preview.cpp @@ -36,7 +36,6 @@ #include "config.h" #include -#include #include #include "ass_dialogue.h" @@ -82,8 +81,8 @@ void SubtitlesPreview::SetStyle(AssStyle const& newStyle) { UpdateBitmap(); } -void SubtitlesPreview::SetText(wxString text) { - wxString newText = "{\\q2}" + text; +void SubtitlesPreview::SetText(std::string const& text) { + std::string newText = "{\\q2}" + text; if (newText != line->Text) { line->Text = newText; UpdateBitmap(); @@ -141,8 +140,8 @@ void SubtitlesPreview::OnSize(wxSizeEvent &evt) { "No subtitles provider", wxOK | wxICON_ERROR | wxCENTER); } - subFile->SetScriptInfo("PlayResX", wxString::Format("%d", w)); - subFile->SetScriptInfo("PlayResY", wxString::Format("%d", h)); + subFile->SetScriptInfo("PlayResX", std::to_string(w)); + subFile->SetScriptInfo("PlayResY", std::to_string(h)); UpdateBitmap(); } diff --git a/aegisub/src/subs_preview.h b/aegisub/src/subs_preview.h index b3c7a1be8..16e1e52dd 100644 --- a/aegisub/src/subs_preview.h +++ b/aegisub/src/subs_preview.h @@ -70,7 +70,7 @@ public: /// Set the style to use void SetStyle(AssStyle const& style); /// Set the text to display - void SetText(wxString text); + void SetText(std::string const& text); /// Set the background color void SetColour(agi::Color col); diff --git a/aegisub/src/subtitle_format.cpp b/aegisub/src/subtitle_format.cpp index 64821c48f..66583d7fb 100644 --- a/aegisub/src/subtitle_format.cpp +++ b/aegisub/src/subtitle_format.cpp @@ -43,6 +43,7 @@ #include "ass_dialogue.h" #include "ass_file.h" #include "ass_style.h" +#include "compat.h" #include "subtitle_format_ass.h" #include "subtitle_format_ebu3264.h" #include "subtitle_format_encore.h" @@ -55,11 +56,17 @@ #include "utils.h" #include "video_context.h" +#include #include +#include +#include +#include +#include + using namespace std::placeholders; -SubtitleFormat::SubtitleFormat(wxString const& name) +SubtitleFormat::SubtitleFormat(std::string const& name) : name(name) { formats.push_back(this); @@ -69,12 +76,16 @@ SubtitleFormat::~SubtitleFormat() { formats.erase(remove(begin(formats), end(formats), this)); } -bool SubtitleFormat::CanReadFile(wxString const& filename) const { - return GetReadWildcards().Index(filename.AfterLast('.'), false) != wxNOT_FOUND; +bool SubtitleFormat::CanReadFile(agi::fs::path const& filename) const { + auto wildcards = GetReadWildcards(); + return any_of(begin(wildcards), end(wildcards), + [&](std::string const& ext) { return agi::fs::HasExtension(filename, ext); }); } -bool SubtitleFormat::CanWriteFile(wxString const& filename) const { - return GetWriteWildcards().Index(filename.AfterLast('.'), false) != wxNOT_FOUND; +bool SubtitleFormat::CanWriteFile(agi::fs::path const& filename) const { + auto wildcards = GetWriteWildcards(); + return any_of(begin(wildcards), end(wildcards), + [&](std::string const& ext) { return agi::fs::HasExtension(filename, ext); }); } bool SubtitleFormat::CanSave(const AssFile *subs) const { @@ -168,14 +179,16 @@ void SubtitleFormat::StripTags(AssFile &file) { current->StripTags(); } -void SubtitleFormat::ConvertNewlines(AssFile &file, wxString const& newline, bool mergeLineBreaks) { +void SubtitleFormat::ConvertNewlines(AssFile &file, std::string const& newline, bool mergeLineBreaks) { for (auto current : file.Line | agi::of_type()) { - wxString repl(current->Text); - repl.Replace("\\h", " "); - repl.Replace("\\n", newline); - repl.Replace("\\N", newline); + std::string repl = current->Text; + boost::replace_all(repl, "\\h", " "); + boost::ireplace_all(repl, "\\n", newline); if (mergeLineBreaks) { - while (repl.Replace(newline+newline, newline)); + std::string dbl(newline + newline); + size_t pos = 0; + while ((pos = repl.find(dbl, pos)) != std::string::npos) + boost::replace_all(repl, dbl, newline); } current->Text = repl; } @@ -184,7 +197,7 @@ void SubtitleFormat::ConvertNewlines(AssFile &file, wxString const& newline, boo void SubtitleFormat::StripComments(AssFile &file) { file.Line.remove_and_dispose_if([](AssEntry const& e) { const AssDialogue *diag = dynamic_cast(&e); - return diag && (diag->Comment || !diag->Text.get()); + return diag && (diag->Comment || diag->Text.get().empty()); }, [](AssEntry *e) { delete e; }); } @@ -242,7 +255,7 @@ void SubtitleFormat::RecombineOverlaps(AssFile &file) { newdlg->Start = curdlg->Start; newdlg->End = (prevdlg->End < curdlg->End) ? prevdlg->End : curdlg->End; // Put an ASS format hard linewrap between lines - newdlg->Text = curdlg->Text + "\\N" + prevdlg->Text; + newdlg->Text = curdlg->Text.get() + "\\N" + prevdlg->Text.get(); file.Line.insert(find_if(next, file.Line.end(), std::bind(dialog_start_lt, _1, newdlg)), *newdlg); } @@ -322,32 +335,30 @@ SubtitleFormat *find_or_throw(Cont &container, Pred pred) { return *it; } -const SubtitleFormat *SubtitleFormat::GetReader(wxString const& filename) { +const SubtitleFormat *SubtitleFormat::GetReader(agi::fs::path const& filename) { LoadFormats(); return find_or_throw(formats, std::bind(&SubtitleFormat::CanReadFile, _1, filename)); } -const SubtitleFormat *SubtitleFormat::GetWriter(wxString const& filename) { +const SubtitleFormat *SubtitleFormat::GetWriter(agi::fs::path const& filename) { LoadFormats(); return find_or_throw(formats, std::bind(&SubtitleFormat::CanWriteFile, _1, filename)); } -wxString SubtitleFormat::GetWildcards(int mode) { +std::string SubtitleFormat::GetWildcards(int mode) { LoadFormats(); - wxArrayString all; - wxString final; + std::vector all; + std::string final; for (auto format : formats) { - wxArrayString cur = mode == 0 ? format->GetReadWildcards() : format->GetWriteWildcards(); + std::vector cur = mode == 0 ? format->GetReadWildcards() : format->GetWriteWildcards(); if (cur.empty()) continue; - for_each(cur.begin(), cur.end(), std::bind(&wxString::Prepend, _1, "*.")); - copy(cur.begin(), cur.end(), std::back_inserter(all)); - final += "|" + format->GetName() + " (" + wxJoin(cur, ',') + ")|" + wxJoin(cur, ';'); + for_each(cur.begin(), cur.end(), [](std::string &str) { str.insert(0, "*."); }); + all.insert(all.end(), begin(cur), end(cur)); + final += "|" + format->GetName() + " (" + boost::join(cur, ",") + ")|" + boost::join(cur, ";"); } - final.Prepend(_("All Supported Formats") + " (" + wxJoin(all, ',') + ")|" + wxJoin(all, ';')); - - return final; + return from_wx(_("All Supported Formats")) + " (" + boost::join(all, ",") + ")|" + boost::join(all, ";") + final; } diff --git a/aegisub/src/subtitle_format.h b/aegisub/src/subtitle_format.h index 62784b5c9..b330d7413 100644 --- a/aegisub/src/subtitle_format.h +++ b/aegisub/src/subtitle_format.h @@ -34,24 +34,23 @@ #pragma once -#include - -#include -#include - #include +#include + +#include +#include class AssEntry; class AssFile; namespace agi { namespace vfr { class Framerate; } } class SubtitleFormat { - wxString name; + std::string name; /// Get this format's wildcards for a load dialog - virtual wxArrayString GetReadWildcards() const { return wxArrayString(); } + virtual std::vector GetReadWildcards() const { return std::vector(); } /// Get this format's wildcards for a save dialog - virtual wxArrayString GetWriteWildcards() const { return wxArrayString(); } + virtual std::vector GetWriteWildcards() const { return std::vector(); } /// List of loaded subtitle formats static std::vector formats; @@ -62,7 +61,7 @@ public: /// Convert newlines to the specified character(s) /// @param lineEnd newline character(s) /// @param mergeLineBreaks Should multiple consecutive line breaks be merged into one? - static void ConvertNewlines(AssFile &file, wxString const& newline, bool mergeLineBreaks = true); + static void ConvertNewlines(AssFile &file, std::string const& newline, bool mergeLineBreaks = true); /// Remove All commented and empty lines static void StripComments(AssFile &file); /// Remove everything but the dialogue lines @@ -82,25 +81,25 @@ public: /// Constructor /// @param Subtitle format name /// @note Automatically registers the format - SubtitleFormat(wxString const& name); + SubtitleFormat(std::string const& name); /// Destructor /// @note Automatically unregisters the format virtual ~SubtitleFormat(); /// Get this format's name - wxString GetName() const { return name; } + std::string const& GetName() const { return name; } /// @brief Check if the given file can be read by this format /// /// Default implementation simply checks if the file's extension is in the /// format's wildcard list - virtual bool CanReadFile(wxString const& filename) const; + virtual bool CanReadFile(agi::fs::path const& filename) const; /// @brief Check if the given file can be written by this format /// /// Default implementation simply checks if the file's extension is in the /// format's wildcard list - virtual bool CanWriteFile(wxString const& filename) const; + virtual bool CanWriteFile(agi::fs::path const& filename) const; /// @brief Check if the given subtitles can be losslessly written by this format /// @@ -112,22 +111,22 @@ public: /// @param[out] target Destination to read lines into /// @param filename File to load /// @param forceEncoding Encoding to use or empty string for default - virtual void ReadFile(AssFile *target, wxString const& filename, wxString const& forceEncoding="") const { } + virtual void ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& forceEncoding="") const { } /// Save a subtitle file /// @param src Data to write /// @param filename File to write to /// @param forceEncoding Encoding to use or empty string for default - virtual void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding="") const { } + virtual void WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding="") const { } /// Get the wildcards for a save or load dialog /// @param mode 0: load 1: save - static wxString GetWildcards(int mode); + static std::string GetWildcards(int mode); /// Get a subtitle format that can read the given file or nullptr if none can - static const SubtitleFormat *GetReader(wxString const& filename); + static const SubtitleFormat *GetReader(agi::fs::path const& filename); /// Get a subtitle format that can write the given file or nullptr if none can - static const SubtitleFormat *GetWriter(wxString const& filename); + static const SubtitleFormat *GetWriter(agi::fs::path const& filename); /// Initialize subtitle formats static void LoadFormats(); /// Deinitialize subtitle formats diff --git a/aegisub/src/subtitle_format_ass.cpp b/aegisub/src/subtitle_format_ass.cpp index bbec0936e..194d3e00e 100644 --- a/aegisub/src/subtitle_format_ass.cpp +++ b/aegisub/src/subtitle_format_ass.cpp @@ -38,11 +38,12 @@ #include "ass_file.h" #include "ass_parser.h" -#include "compat.h" #include "text_file_reader.h" #include "text_file_writer.h" #include "version.h" +#include + DEFINE_SIMPLE_EXCEPTION(AssParseError, SubtitleFormatParseError, "subtitle_io/parse/ass") AssSubtitleFormat::AssSubtitleFormat() @@ -50,35 +51,32 @@ AssSubtitleFormat::AssSubtitleFormat() { } -wxArrayString AssSubtitleFormat::GetReadWildcards() const { - wxArrayString formats; - formats.Add("ass"); - formats.Add("ssa"); +std::vector AssSubtitleFormat::GetReadWildcards() const { + std::vector formats; + formats.push_back("ass"); + formats.push_back("ssa"); return formats; } -wxArrayString AssSubtitleFormat::GetWriteWildcards() const { - wxArrayString formats; - formats.Add("ass"); - formats.Add("ssa"); +std::vector AssSubtitleFormat::GetWriteWildcards() const { + std::vector formats; + formats.push_back("ass"); + formats.push_back("ssa"); return formats; } -void AssSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxString const& encoding) const { - target->Clear(); - +void AssSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& encoding) const { TextFileReader file(filename, encoding); - int version = filename.Right(4).Lower() != ".ssa"; + int version = !agi::fs::HasExtension(filename, "ssa"); AssParser parser(target, version); while (file.HasMoreLines()) { - wxString line = file.ReadLineFromFile(); + std::string line = file.ReadLineFromFile(); try { parser.AddLine(line); } catch (const char *err) { - target->Clear(); - throw AssParseError("Error processing line: " + from_wx(line) + ": " + err, 0); + throw AssParseError("Error processing line: " + line + ": " + err, 0); } } } @@ -89,7 +87,7 @@ void AssSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxSt #define LINEBREAK "\n" #endif -static inline wxString format(AssEntryGroup group, bool ssa) { +static inline std::string format(AssEntryGroup group, bool ssa) { if (group == ENTRY_DIALOGUE) { if (ssa) return "Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" LINEBREAK; @@ -104,17 +102,17 @@ static inline wxString format(AssEntryGroup group, bool ssa) { return "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding" LINEBREAK; } - return wxS(""); + return ""; } -void AssSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const { +void AssSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const { TextFileWriter file(filename, encoding); file.WriteLineToFile("[Script Info]"); - file.WriteLineToFile(wxString("; Script generated by Aegisub ") + GetAegisubLongVersionString()); + file.WriteLineToFile(std::string("; Script generated by Aegisub ") + GetAegisubLongVersionString()); file.WriteLineToFile("; http://www.aegisub.org/"); - bool ssa = filename.Right(4).Lower() == ".ssa"; + bool ssa = agi::fs::HasExtension(filename, "ssa"); AssEntryGroup group = ENTRY_INFO; for (auto const& line : src->Line) { diff --git a/aegisub/src/subtitle_format_ass.h b/aegisub/src/subtitle_format_ass.h index f51954917..b3fbda8b9 100644 --- a/aegisub/src/subtitle_format_ass.h +++ b/aegisub/src/subtitle_format_ass.h @@ -38,12 +38,12 @@ class AssSubtitleFormat : public SubtitleFormat { public: AssSubtitleFormat(); - wxArrayString GetReadWildcards() const override; - wxArrayString GetWriteWildcards() const override; + std::vector GetReadWildcards() const override; + std::vector GetWriteWildcards() const override; // Naturally the ASS subtitle format can save all Ass files bool CanSave(const AssFile*) const override { return true; } - void ReadFile(AssFile *target, wxString const& filename, wxString const& forceEncoding) const override; - void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const override; + void ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& forceEncoding) const override; + void WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const override; }; diff --git a/aegisub/src/subtitle_format_ebu3264.cpp b/aegisub/src/subtitle_format_ebu3264.cpp index babac3f5d..908ce4f51 100644 --- a/aegisub/src/subtitle_format_ebu3264.cpp +++ b/aegisub/src/subtitle_format_ebu3264.cpp @@ -25,8 +25,6 @@ #include "subtitle_format_ebu3264.h" -#include - #include #include #include @@ -43,6 +41,8 @@ #include "options.h" #include "text_file_writer.h" +#include + namespace { #pragma pack(push, 1) @@ -101,11 +101,11 @@ namespace /// A block of text with basic formatting information struct EbuFormattedText { - wxString text; ///< Text in this block - bool underline; ///< Is this block underlined? - bool italic; ///< Is this block italic? - bool word_start; ///< Is it safe to line-wrap between this block and the previous one? - EbuFormattedText(wxString const& t, bool u = false, bool i = false, bool ws = true) : text(t), underline(u), italic(i), word_start(ws) { } + std::string text; ///< Text in this block + bool underline; ///< Is this block underlined? + bool italic; ///< Is this block italic? + bool word_start; ///< Is it safe to line-wrap between this block and the previous one? + EbuFormattedText(std::string const& t, bool u = false, bool i = false, bool ws = true) : text(t), underline(u), italic(i), word_start(ws) { } }; typedef std::vector EbuTextRow; @@ -262,9 +262,6 @@ namespace void SetTextFromAss(AssDialogue *line, bool style_underline, bool style_italic, int align, int wrap_mode) { - // Helper for finding special characters - wxRegEx special_char_search("\\\\[nN]| ", wxRE_ADVANCED); - boost::ptr_vector blocks(line->ParseTags()); text_rows.clear(); @@ -285,42 +282,42 @@ namespace case BLOCK_PLAIN: // find special characters and convert them { - wxString text = to_wx(b.GetText()); + std::string text = b.GetText(); - text.Replace("\\t", " "); + boost::replace_all(text, "\\t", " "); - while (special_char_search.Matches(text)) + size_t start = 0; + for (size_t i = 0; i < text.size(); ++i) { - size_t start, len; - special_char_search.GetMatch(&start, &len); + if (text[i] != ' ' && (i + 1 >= text.size() || text[i] != '\\' || (text[i + 1] != 'N' && text[i + 1] != 'n'))) + continue; // add first part of text to current part - cur_row->back().text.append(text.Left(start)); + cur_row->back().text.append(text.substr(start, i)); // process special character - wxString substr = text.Mid(start, len); - if (substr == "\\N" || (wrap_mode == 1 && substr == "\\n")) + if (text[i] == '\\' && (text[i + 1] == 'N' || wrap_mode == 1)) { // create a new row with current style text_rows.emplace_back(); cur_row = &text_rows.back(); cur_row->emplace_back("", underline, italic, true); } - else // if (substr == " " || substr == "\\h" || substr == "\\n") + else // if (substr == " " || substr == "\\n") { cur_row->back().text.append(" "); cur_row->emplace_back("", underline, italic, true); } - text = text.Mid(start+len); + start = i + (text[i] == '\\'); } // add the remaining text - cur_row->back().text.append(text); + cur_row->back().text.append(text.substr(start)); // convert \h to regular spaces // done after parsing so that words aren't split on \h - cur_row->back().text.Replace("\\h", " "); + boost::replace_all(cur_row->back().text, "\\h", " "); } break; @@ -334,7 +331,7 @@ namespace // apply any changes if (underline != cur_row->back().underline || italic != cur_row->back().italic) { - if (!cur_row->back().text) + if (cur_row->back().text.empty()) { // current part is empty, we can safely change formatting on it cur_row->back().underline = underline; @@ -343,7 +340,7 @@ namespace else { // create a new empty part with new style - cur_row->push_back(EbuFormattedText("", underline, italic, false)); + cur_row->emplace_back("", underline, italic, false); } } } @@ -396,7 +393,7 @@ namespace imline.time_out -= 1; // convert alignment from style - AssStyle *style = copy.GetStyle(from_wx(line->Style)); + AssStyle *style = copy.GetStyle(line->Style); if (!style) style = &default_style; @@ -429,20 +426,6 @@ namespace return subs_list; } - inline size_t buffer_size(wxString const& str) - { -#if wxUSE_UNICODE_UTF8 - return str.utf8_length(); -#else - return str.length() * sizeof(wxStringCharType); -#endif - } - - inline const char *wx_str(wxString const& str) - { - return reinterpret_cast(str.wx_str()); - } - std::string convert_subtitle_line(EbuSubtitle const& sub, agi::charset::IconvWrapper *encoder, bool enable_formatting) { std::string fullstring; @@ -468,7 +451,7 @@ namespace } // convert text to specified encoding - fullstring += encoder->Convert(std::string(wx_str(block.text), buffer_size(block.text))); + fullstring += encoder->Convert(block.text); } } return fullstring; @@ -564,11 +547,11 @@ namespace BlockGSI create_header(AssFile const& copy, EbuExportSettings const& export_settings) { - wxString scriptinfo_title = copy.GetScriptInfo("Title"); - wxString scriptinfo_translation = copy.GetScriptInfo("Original Translation"); - wxString scriptinfo_editing = copy.GetScriptInfo("Original Editing"); + std::string scriptinfo_title = copy.GetScriptInfo("Title"); + std::string scriptinfo_translation = copy.GetScriptInfo("Original Translation"); + std::string scriptinfo_editing = copy.GetScriptInfo("Original Editing"); - agi::charset::IconvWrapper gsi_encoder(wxSTRING_ENCODING, "CP850"); + agi::charset::IconvWrapper gsi_encoder("UTF-8", "CP850"); BlockGSI gsi; memset(&gsi, 0x20, sizeof(gsi)); // fill with spaces @@ -595,8 +578,8 @@ namespace if (export_settings.text_encoding == EbuExportSettings::utf8) memcpy(gsi.cct, "U8", 2); memcpy(gsi.lc, "00", 2); - gsi_encoder.Convert(wx_str(scriptinfo_title), buffer_size(scriptinfo_title), gsi.opt, 32); - gsi_encoder.Convert(wx_str(scriptinfo_translation), buffer_size(scriptinfo_translation), gsi.tn, 32); + gsi_encoder.Convert(scriptinfo_title.c_str(), scriptinfo_title.size(), gsi.opt, 32); + gsi_encoder.Convert(scriptinfo_translation.c_str(), scriptinfo_translation.size(), gsi.tn, 32); { char buf[20]; time_t now; @@ -618,7 +601,7 @@ namespace gsi.tnd = '1'; gsi.dsn = '1'; memcpy(gsi.co, "NTZ", 3); // neutral zone! - gsi_encoder.Convert(wx_str(scriptinfo_editing), buffer_size(scriptinfo_editing), gsi.en, 32); + gsi_encoder.Convert(scriptinfo_editing.c_str(), scriptinfo_editing.size(), gsi.en, 32); if (export_settings.text_encoding == EbuExportSettings::utf8) strncpy(gsi.uda, "This file was exported by Aegisub using non-standard UTF-8 encoding for the subtitle blocks. The TTI.TF field contains UTF-8-encoded text interspersed with the standard formatting codes, which are not encoded. GSI.CCT is set to 'U8' to signify this.", sizeof(gsi.uda)); @@ -648,14 +631,14 @@ Ebu3264SubtitleFormat::Ebu3264SubtitleFormat() { } -wxArrayString Ebu3264SubtitleFormat::GetWriteWildcards() const +std::vector Ebu3264SubtitleFormat::GetWriteWildcards() const { - wxArrayString formats; - formats.Add("stl"); + std::vector formats; + formats.push_back("stl"); return formats; } -void Ebu3264SubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const +void Ebu3264SubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const&) const { // collect data from user EbuExportSettings export_settings = get_export_config(0); @@ -671,7 +654,7 @@ void Ebu3264SubtitleFormat::WriteFile(const AssFile *src, wxString const& filena snprintf(gsi.tns, 5, "%5u", (unsigned int)subs_list.size()); // write file - agi::io::Save f(from_wx(filename), true); + agi::io::Save f(filename, true); f.Get().write((const char *)&gsi, sizeof(gsi)); for (auto const& block : tti) f.Get().write((const char *)&block, sizeof(block)); diff --git a/aegisub/src/subtitle_format_ebu3264.h b/aegisub/src/subtitle_format_ebu3264.h index 04179f9aa..72b480e35 100644 --- a/aegisub/src/subtitle_format_ebu3264.h +++ b/aegisub/src/subtitle_format_ebu3264.h @@ -27,8 +27,8 @@ class Ebu3264SubtitleFormat : public SubtitleFormat { public: Ebu3264SubtitleFormat(); - wxArrayString GetWriteWildcards() const override; - void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const override; + std::vector GetWriteWildcards() const override; + void WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const override; DEFINE_SIMPLE_EXCEPTION(ConversionFailed, agi::InvalidInputException, "subtitle_io/ebu3264/conversion_error") }; diff --git a/aegisub/src/subtitle_format_encore.cpp b/aegisub/src/subtitle_format_encore.cpp index 2312d765f..13796a355 100644 --- a/aegisub/src/subtitle_format_encore.cpp +++ b/aegisub/src/subtitle_format_encore.cpp @@ -42,22 +42,25 @@ #include +#include +#include + EncoreSubtitleFormat::EncoreSubtitleFormat() : SubtitleFormat("Adobe Encore") { } -wxArrayString EncoreSubtitleFormat::GetWriteWildcards() const { - wxArrayString formats; - formats.Add("encore.txt"); +std::vector EncoreSubtitleFormat::GetWriteWildcards() const { + std::vector formats; + formats.push_back("encore.txt"); return formats; } -bool EncoreSubtitleFormat::CanWriteFile(wxString const& filename) const { - return filename.EndsWith(".encore.txt"); +bool EncoreSubtitleFormat::CanWriteFile(agi::fs::path const& filename) const { + return boost::iends_with(filename.string(), ".encore.txt"); } -void EncoreSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const&) const { +void EncoreSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const&) const { agi::vfr::Framerate fps = AskForFPS(false, true); if (!fps.IsLoaded()) return; @@ -70,14 +73,13 @@ void EncoreSubtitleFormat::WriteFile(const AssFile *src, wxString const& filenam StripTags(copy); ConvertNewlines(copy, "\r\n"); - // Encode wants ; for NTSC and : for PAL + // Encore wants ; for NTSC and : for PAL // The manual suggests no other frame rates are supported - char sep = fps.NeedsDropFrames() ? ';' : ':'; - SmpteFormatter ft(fps, sep); + SmpteFormatter ft(fps, fps.NeedsDropFrames() ? ";" : ":"); // Write lines int i = 0; TextFileWriter file(filename, "UTF-8"); for (auto current : copy.Line | agi::of_type()) - file.WriteLineToFile(wxString::Format("%i %s %s %s", ++i, ft.ToSMPTE(current->Start), ft.ToSMPTE(current->End), current->Text.get())); + file.WriteLineToFile(str(boost::format("%i %s %s %s") % ++i % ft.ToSMPTE(current->Start) % ft.ToSMPTE(current->End) % current->Text)); } diff --git a/aegisub/src/subtitle_format_encore.h b/aegisub/src/subtitle_format_encore.h index 73ce73063..109e8ea73 100644 --- a/aegisub/src/subtitle_format_encore.h +++ b/aegisub/src/subtitle_format_encore.h @@ -37,7 +37,7 @@ class EncoreSubtitleFormat : public SubtitleFormat { public: EncoreSubtitleFormat(); - wxArrayString GetWriteWildcards() const override; - bool CanWriteFile(wxString const& filename) const override; - void WriteFile(const AssFile *src, wxString const& filename, wxString const&) const override; + std::vector GetWriteWildcards() const override; + bool CanWriteFile(agi::fs::path const& filename) const override; + void WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const&) const override; }; diff --git a/aegisub/src/subtitle_format_microdvd.cpp b/aegisub/src/subtitle_format_microdvd.cpp index 0468de18a..bf2756bfe 100644 --- a/aegisub/src/subtitle_format_microdvd.cpp +++ b/aegisub/src/subtitle_format_microdvd.cpp @@ -36,8 +36,6 @@ #include "subtitle_format_microdvd.h" -#include - #include "ass_dialogue.h" #include "ass_file.h" #include "ass_time.h" @@ -45,40 +43,46 @@ #include "text_file_writer.h" #include "video_context.h" +#include #include +#include + +#include +#include +#include +#include MicroDVDSubtitleFormat::MicroDVDSubtitleFormat() : SubtitleFormat("MicroDVD") { } -wxArrayString MicroDVDSubtitleFormat::GetReadWildcards() const { - wxArrayString formats; - formats.Add("sub"); +std::vector MicroDVDSubtitleFormat::GetReadWildcards() const { + std::vector formats; + formats.push_back("sub"); return formats; } -wxArrayString MicroDVDSubtitleFormat::GetWriteWildcards() const { +std::vector MicroDVDSubtitleFormat::GetWriteWildcards() const { return GetReadWildcards(); } -bool MicroDVDSubtitleFormat::CanReadFile(wxString const& filename) const { +static const boost::regex line_regex("^[\\{\\[]([0-9]+)[\\}\\]][\\{\\[]([0-9]+)[\\}\\]](.*)$"); + +bool MicroDVDSubtitleFormat::CanReadFile(agi::fs::path const& filename) const { // Return false immediately if extension is wrong - if (filename.Right(4).Lower() != ".sub") return false; + if (!agi::fs::HasExtension(filename, "sub")) return false; // Since there is an infinity of .sub formats, load first line and check if it's valid TextFileReader file(filename); - if (file.HasMoreLines()) { - wxRegEx exp("^[\\{\\[]([0-9]+)[\\}\\]][\\{\\[]([0-9]+)[\\}\\]](.*)$", wxRE_ADVANCED); - return exp.Matches(file.ReadLineFromFile()); - } + if (file.HasMoreLines()) + return regex_match(file.ReadLineFromFile(), line_regex); return false; } -void MicroDVDSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxString const& encoding) const { +void MicroDVDSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& encoding) const { TextFileReader file(filename, encoding); - wxRegEx exp("^[\\{\\[]([0-9]+)[\\}\\]][\\{\\[]([0-9]+)[\\}\\]](.*)$", wxRE_ADVANCED); target->LoadDefault(false); @@ -86,24 +90,21 @@ void MicroDVDSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, bool isFirst = true; while (file.HasMoreLines()) { - wxString line = file.ReadLineFromFile(); - if (exp.Matches(line)) { - long f1, f2; - exp.GetMatch(line, 1).ToLong(&f1); - exp.GetMatch(line, 2).ToLong(&f2); - wxString text = exp.GetMatch(line, 3); + boost::smatch match; + std::string line = file.ReadLineFromFile(); + if (regex_match(line, match, line_regex)) { + int f1 = boost::lexical_cast(match[0]); + int f2 = boost::lexical_cast(match[1]); + std::string text = match[2].str(); // If it's the first, check if it contains fps information if (isFirst) { isFirst = false; - if (f1 == 1 && f2 == 1) { - // Convert fps - double cfr; - if (text.ToDouble(&cfr)) { - fps = cfr; - continue; - } + double cfr; + if (agi::util::try_parse(text, &cfr)) { + fps = cfr; + continue; } // If it wasn't an fps line, ask the user for it @@ -111,7 +112,7 @@ void MicroDVDSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, if (!fps.IsLoaded()) return; } - text.Replace("|", "\\N"); + boost::replace_all(text, "|", "\\N"); AssDialogue *diag = new AssDialogue; diag->Start = fps.TimeAtFrame(f1, agi::vfr::START); @@ -122,7 +123,7 @@ void MicroDVDSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, } } -void MicroDVDSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const { +void MicroDVDSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const { agi::vfr::Framerate fps = AskForFPS(true, false); if (!fps.IsLoaded()) return; @@ -137,15 +138,14 @@ void MicroDVDSubtitleFormat::WriteFile(const AssFile *src, wxString const& filen TextFileWriter file(filename, encoding); // Write FPS line - if (!fps.IsVFR()) { - file.WriteLineToFile(wxString::Format("{1}{1}%.6f", fps.FPS())); - } + if (!fps.IsVFR()) + file.WriteLineToFile(str(boost::format("{1}{1}%.6f") % fps.FPS())); // Write lines for (auto current : copy.Line | agi::of_type()) { int start = fps.FrameAtTime(current->Start, agi::vfr::START); int end = fps.FrameAtTime(current->End, agi::vfr::END); - file.WriteLineToFile(wxString::Format("{%i}{%i}%s", start, end, current->Text.get())); + file.WriteLineToFile(str(boost::format("{%i}{%i}%s") % start % end % boost::replace_all_copy(current->Text.get(), "\\N", "|"))); } } diff --git a/aegisub/src/subtitle_format_microdvd.h b/aegisub/src/subtitle_format_microdvd.h index 19833808a..96fc4017a 100644 --- a/aegisub/src/subtitle_format_microdvd.h +++ b/aegisub/src/subtitle_format_microdvd.h @@ -38,11 +38,11 @@ class MicroDVDSubtitleFormat : public SubtitleFormat { public: MicroDVDSubtitleFormat(); - wxArrayString GetReadWildcards() const override; - wxArrayString GetWriteWildcards() const override; + std::vector GetReadWildcards() const override; + std::vector GetWriteWildcards() const override; - bool CanReadFile(wxString const& filename) const override; - void ReadFile(AssFile *target, wxString const& filename, wxString const& forceEncoding) const override; + bool CanReadFile(agi::fs::path const& filename) const override; + void ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& forceEncoding) const override; - void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const override; + void WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const override; }; diff --git a/aegisub/src/subtitle_format_mkv.cpp b/aegisub/src/subtitle_format_mkv.cpp index 99af9b209..a4f10a587 100644 --- a/aegisub/src/subtitle_format_mkv.cpp +++ b/aegisub/src/subtitle_format_mkv.cpp @@ -43,14 +43,14 @@ MKVSubtitleFormat::MKVSubtitleFormat() { } -wxArrayString MKVSubtitleFormat::GetReadWildcards() const { - wxArrayString formats; - formats.Add("mkv"); - formats.Add("mka"); - formats.Add("mks"); +std::vector MKVSubtitleFormat::GetReadWildcards() const { + std::vector formats; + formats.push_back("mkv"); + formats.push_back("mka"); + formats.push_back("mks"); return formats; } -void MKVSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxString const&) const { +void MKVSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, std::string const&) const { MatroskaWrapper::GetSubtitles(filename, target); } diff --git a/aegisub/src/subtitle_format_mkv.h b/aegisub/src/subtitle_format_mkv.h index 1026d07e0..31a1630bd 100644 --- a/aegisub/src/subtitle_format_mkv.h +++ b/aegisub/src/subtitle_format_mkv.h @@ -37,7 +37,7 @@ class MKVSubtitleFormat : public SubtitleFormat { public: MKVSubtitleFormat(); - wxArrayString GetReadWildcards() const override; + std::vector GetReadWildcards() const override; - void ReadFile(AssFile *target, wxString const& filename, wxString const& forceEncoding) const override; + void ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& forceEncoding) const override; }; diff --git a/aegisub/src/subtitle_format_srt.cpp b/aegisub/src/subtitle_format_srt.cpp index 1cd092e7e..fd5112fc5 100644 --- a/aegisub/src/subtitle_format_srt.cpp +++ b/aegisub/src/subtitle_format_srt.cpp @@ -40,25 +40,28 @@ #include "ass_dialogue.h" #include "ass_file.h" #include "ass_style.h" -#include "colorspace.h" -#include "compat.h" #include "utils.h" #include "text_file_reader.h" #include "text_file_writer.h" #include +#include +#include +#include +#include +#include +#include #include -#include DEFINE_SIMPLE_EXCEPTION(SRTParseError, SubtitleFormatParseError, "subtitle_io/parse/srt") namespace { class SrtTagParser { struct FontAttribs { - wxString face; - wxString size; - wxString color; + std::string face; + std::string size; + std::string color; }; enum TagType { @@ -75,20 +78,17 @@ class SrtTagParser { TAG_FONT_CLOSE }; - wxRegEx tag_matcher; - wxRegEx attrib_matcher; - std::map tag_name_cases; + const boost::regex tag_matcher; + const boost::regex attrib_matcher; + const boost::regex is_quoted; + std::map tag_name_cases; public: SrtTagParser() - : tag_matcher("^(.*?)<(/?b|/?i|/?u|/?s|/?font)([^>]*)>(.*)$", wxRE_ICASE|wxRE_ADVANCED) - , attrib_matcher("^[[:space:]]+(face|size|color)=('[^']*'|\"[^\"]*\"|[^[:space:]]+)", wxRE_ICASE|wxRE_ADVANCED) + : tag_matcher("^(.*?)<(/?b|/?i|/?u|/?s|/?font)([^>]*)>(.*)$", boost::regex::icase) + , attrib_matcher("^[[:space:]]+(face|size|color)=('[^']*'|\"[^\"]*\"|[^[:space:]]+)", boost::regex::icase) + , is_quoted("^(['\"]).*\\1$") { - if (!tag_matcher.IsValid()) - throw agi::InternalError("Parsing SRT: Failed compiling tag matching regex", 0); - if (!attrib_matcher.IsValid()) - throw agi::InternalError("Parsing SRT: Failed compiling tag attribute matching regex", 0); - tag_name_cases["b"] = TAG_BOLD_OPEN; tag_name_cases["/b"] = TAG_BOLD_CLOSE; tag_name_cases["i"] = TAG_ITALICS_OPEN; @@ -101,7 +101,7 @@ public: tag_name_cases["/font"] = TAG_FONT_CLOSE; } - wxString ToAss(wxString srt) + std::string ToAss(std::string srt) { int bold_level = 0; int italics_level = 0; @@ -109,11 +109,12 @@ public: int strikeout_level = 0; std::vector font_stack; - wxString ass; // result to be built + std::string ass; // result to be built while (!srt.empty()) { - if (!tag_matcher.Matches(srt)) + boost::smatch result; + if (!regex_match(srt, result, tag_matcher)) { // no more tags could be matched, end of string ass.append(srt); @@ -121,17 +122,18 @@ public: } // we found a tag, translate it - wxString pre_text = tag_matcher.GetMatch(srt, 1); - wxString tag_name = tag_matcher.GetMatch(srt, 2); - wxString tag_attrs = tag_matcher.GetMatch(srt, 3); - wxString post_text = tag_matcher.GetMatch(srt, 4); + std::string pre_text = result.str(1); + std::string tag_name = result.str(2); + std::string tag_attrs = result.str(3); + std::string post_text = result.str(4); // the text before the tag goes through unchanged ass.append(pre_text); // the text after the tag is the input for next iteration srt = post_text; - switch (tag_name_cases[tag_name.Lower()]) + boost::to_lower(tag_name); + switch (tag_name_cases[tag_name]) { case TAG_BOLD_OPEN: if (bold_level == 0) @@ -184,41 +186,33 @@ public: FontAttribs old_attribs; // start out with any previous ones on stack if (font_stack.size() > 0) - { old_attribs = font_stack.back(); - } new_attribs = old_attribs; // now find all attributes on this font tag - while (attrib_matcher.Matches(tag_attrs)) + boost::smatch result; + while (regex_search(tag_attrs, result, attrib_matcher)) { // get attribute name and values - wxString attr_name = attrib_matcher.GetMatch(tag_attrs, 1); - wxString attr_value = attrib_matcher.GetMatch(tag_attrs, 2); + std::string attr_name = result.str(1); + std::string attr_value = result.str(2); + // clean them - attr_name.MakeLower(); - if ((attr_value.StartsWith("'") && attr_value.EndsWith("'")) || - (attr_value.StartsWith("\"") && attr_value.EndsWith("\""))) - { - attr_value = attr_value.Mid(1, attr_value.Len()-2); - } + boost::to_lower(attr_name); + if (regex_match(attr_value, is_quoted)) + attr_value = attr_value.substr(1, attr_value.size() - 2); + // handle the attributes if (attr_name == "face") - { - new_attribs.face = wxString::Format("{\\fn%s}", attr_value); - } + new_attribs.face = str(boost::format("{\\fn%s}") % attr_value); else if (attr_name == "size") - { - new_attribs.size = wxString::Format("{\\fs%s}", attr_value); - } + new_attribs.size = str(boost::format("{\\fs%s}") % attr_value); else if (attr_name == "color") - { - new_attribs.color = wxString::Format("{\\c%s}", to_wx(agi::Color(from_wx(attr_value)).GetAssOverrideFormatted())); - } + new_attribs.color = str(boost::format("{\\c%s}") % agi::Color(attr_value).GetAssOverrideFormatted()); + // remove this attribute to prepare for the next - size_t attr_pos, attr_len; - attrib_matcher.GetMatch(&attr_pos, &attr_len, 0); - tag_attrs.erase(attr_pos, attr_len); + tag_attrs = result.suffix().str(); } + // the attributes changed from old are then written out if (new_attribs.face != old_attribs.face) ass.append(new_attribs.face); @@ -226,6 +220,7 @@ public: ass.append(new_attribs.size); if (new_attribs.color != old_attribs.color) ass.append(new_attribs.color); + // lastly dump the new attributes state onto the stack font_stack.push_back(new_attribs); } @@ -275,13 +270,13 @@ public: } // make it a little prettier, join tag groups - ass.Replace("}{", "", true); + boost::replace_all(ass, "}{", ""); return ass; } }; -AssTime ReadSRTTime(wxString const& ts) +AssTime ReadSRTTime(std::string const& ts) { // For the sake of your sanity, please do not read this function. @@ -291,22 +286,12 @@ AssTime ReadSRTTime(wxString const& ts) size_t ci = 0; int ms_chars = 0; - for (; ci < ts.length(); ++ci) + for (; ci < ts.size(); ++ci) { - char ch = ts[ci]; - switch (ch) + switch (ts[ci]) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - s = s * 10 + (ch - '0'); + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + s = s * 10 + (ts[ci] - '0'); break; case ':': d = h; @@ -318,43 +303,27 @@ AssTime ReadSRTTime(wxString const& ts) ci++; goto milliseconds; default: - goto allparsed; + ci = ts.size(); } } - goto allparsed; + milliseconds: - for (; ci < ts.length(); ++ci) + for (; ci < ts.size(); ++ci) { - char ch = ts[ci]; - switch (ch) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - ms = ms * 10 + (ch - '0'); - ms_chars++; - break; - default: - goto allparsed; - } + if (!isdigit(ts[ci])) break; + + ms = ms * 10 + (ts[ci] - '0'); + ms_chars++; } -allparsed: - while (ms_chars < 3) ms *= 10, ms_chars++; - while (ms_chars > 3) ms /= 10, ms_chars--; + + ms *= pow(10, 3 - ms_chars); return ms + 1000*(s + 60*(m + 60*(h + d*24))); } -wxString WriteSRTTime(AssTime const& ts) +std::string WriteSRTTime(AssTime const& ts) { - return wxString::Format("%02d:%02d:%02d,%03d", ts.GetTimeHours(), ts.GetTimeMinutes(), ts.GetTimeSeconds(), ts.GetTimeMiliseconds()); + return str(boost::format("%02d:%02d:%02d,%03d") % ts.GetTimeHours() % ts.GetTimeMinutes() % ts.GetTimeSeconds() % ts.GetTimeMiliseconds()); } } @@ -364,13 +333,13 @@ SRTSubtitleFormat::SRTSubtitleFormat() { } -wxArrayString SRTSubtitleFormat::GetReadWildcards() const { - wxArrayString formats; - formats.Add("srt"); +std::vector SRTSubtitleFormat::GetReadWildcards() const { + std::vector formats; + formats.push_back("srt"); return formats; } -wxArrayString SRTSubtitleFormat::GetWriteWildcards() const { +std::vector SRTSubtitleFormat::GetWriteWildcards() const { return GetReadWildcards(); } @@ -382,7 +351,7 @@ enum ParseState { STATE_LAST_WAS_BLANK }; -void SRTSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxString const& encoding) const { +void SRTSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& encoding) const { using namespace std; TextFileReader file(filename, encoding); @@ -391,9 +360,7 @@ void SRTSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxSt // See parsing algorithm at // "hh:mm:ss,fff --> hh:mm:ss,fff" (e.g. "00:00:04,070 --> 00:00:10,04") - wxRegEx timestamp_regex("^([0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{1,}) --> ([0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{1,})"); - if (!timestamp_regex.IsValid()) - throw agi::InternalError("Parsing SRT: Failed compiling regex", 0); + const boost::regex timestamp_regex("^([0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{1,}) --> ([0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{1,})"); SrtTagParser tag_parser; @@ -401,43 +368,47 @@ void SRTSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxSt int line_num = 0; int linebreak_debt = 0; AssDialogue *line = 0; - wxString text; + std::string text; while (file.HasMoreLines()) { - wxString text_line = file.ReadLineFromFile(); - line_num++; - text_line.Trim(true).Trim(false); + std::string text_line = file.ReadLineFromFile(); + ++line_num; + boost::trim(text_line); + boost::smatch timestamp_match; switch (state) { case STATE_INITIAL: // ignore leading blank lines if (text_line.empty()) break; - if (text_line.IsNumber()) { + if (all(text_line, boost::is_digit())) { // found the line number, throw it away and hope for timestamps state = STATE_TIMESTAMP; break; } - if (timestamp_regex.Matches(text_line)) + if (regex_match(text_line, timestamp_match, timestamp_regex)) goto found_timestamps; - throw SRTParseError(from_wx(wxString::Format("Parsing SRT: Expected subtitle index at line %d", line_num)), 0); + throw SRTParseError(str(boost::format("Parsing SRT: Expected subtitle index at line %d") % line_num), 0); + case STATE_TIMESTAMP: - if (!timestamp_regex.Matches(text_line)) - throw SRTParseError(from_wx(wxString::Format("Parsing SRT: Expected timestamp pair at line %d", line_num)), 0); + if (!regex_match(text_line, timestamp_match, timestamp_regex)) + throw SRTParseError(str(boost::format("Parsing SRT: Expected timestamp pair at line %d") % line_num), 0); found_timestamps: if (line) { // finalize active line line->Text = tag_parser.ToAss(text); text.clear(); } + // create new subtitle line = new AssDialogue; - line->Start = ReadSRTTime(timestamp_regex.GetMatch(text_line, 1)); - line->End = ReadSRTTime(timestamp_regex.GetMatch(text_line, 2)); + line->Start = ReadSRTTime(timestamp_match.str(1)); + line->End = ReadSRTTime(timestamp_match.str(2)); // store pointer to subtitle, we'll continue working on it target->Line.push_back(*line); // next we're reading the text state = STATE_FIRST_LINE_OF_BODY; break; + case STATE_FIRST_LINE_OF_BODY: if (text_line.empty()) { // that's not very interesting... blank subtitle? @@ -446,9 +417,10 @@ found_timestamps: linebreak_debt = 0; break; } - text.Append(text_line); + text.append(text_line); state = STATE_REST_OF_BODY; break; + case STATE_REST_OF_BODY: if (text_line.empty()) { // Might be either the gap between two subtitles or just a @@ -458,25 +430,27 @@ found_timestamps: linebreak_debt = 1; break; } - text.Append("\\N").Append(text_line); + text.append("\\N"); + text.append(text_line); break; + case STATE_LAST_WAS_BLANK: ++linebreak_debt; if (text_line.empty()) break; - if (text_line.IsNumber()) { + if (all(text_line, boost::is_digit())) { // Hopefully it's the start of a new subtitle, and the // previous blank line(s) were the gap between subtitles state = STATE_TIMESTAMP; break; } - if (timestamp_regex.Matches(text_line)) + if (regex_match(text_line, timestamp_match, timestamp_regex)) goto found_timestamps; // assume it's a continuation of the subtitle text // resolve our line break debt and append the line text while (linebreak_debt-- > 0) - text.Append("\\N"); - text.Append(text_line); + text.append("\\N"); + text.append(text_line); state = STATE_REST_OF_BODY; break; } @@ -485,12 +459,11 @@ found_timestamps: if (state == 1 || state == 2) throw SRTParseError("Parsing SRT: Incomplete file", 0); - if (line) - // an unfinalized line + if (line) // an unfinalized line line->Text = tag_parser.ToAss(text); } -void SRTSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const { +void SRTSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const { TextFileWriter file(filename, encoding); // Convert to SRT @@ -508,7 +481,7 @@ void SRTSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, // Write lines int i=0; for (auto current : copy.Line | agi::of_type()) { - file.WriteLineToFile(wxString::Format("%d", ++i)); + file.WriteLineToFile(std::to_string(++i)); file.WriteLineToFile(WriteSRTTime(current->Start) + " --> " + WriteSRTTime(current->End)); file.WriteLineToFile(ConvertTags(current)); file.WriteLineToFile(""); @@ -545,8 +518,8 @@ bool SRTSubtitleFormat::CanSave(const AssFile *file) const { return true; } -wxString SRTSubtitleFormat::ConvertTags(const AssDialogue *diag) const { - wxString final; +std::string SRTSubtitleFormat::ConvertTags(const AssDialogue *diag) const { + std::string final; std::map tag_states; tag_states['i'] = false; tag_states['b'] = false; @@ -564,25 +537,24 @@ wxString SRTSubtitleFormat::ConvertTags(const AssDialogue *diag) const { if (it != tag_states.end()) { bool temp = tag.Params[0].Get(false); if (temp && !it->second) - final += wxString::Format("<%c>", it->first); + final += str(boost::format("<%c>") % it->first); if (!temp && it->second) - final += wxString::Format("", it->first); + final += str(boost::format("") % it->first); it->second = temp; } } } } // Plain text - else if (AssDialogueBlockPlain *plain = dynamic_cast(&block)) { - final += to_wx(plain->GetText()); - } + else if (AssDialogueBlockPlain *plain = dynamic_cast(&block)) + final += plain->GetText(); } // Ensure all tags are closed // Otherwise unclosed overrides might affect lines they shouldn't, see bug #809 for example for (auto it : tag_states) { if (it.second) - final += wxString::Format("", it.first); + final += str(boost::format("") % it.first); } return final; diff --git a/aegisub/src/subtitle_format_srt.h b/aegisub/src/subtitle_format_srt.h index f47a908bd..70bb027ea 100644 --- a/aegisub/src/subtitle_format_srt.h +++ b/aegisub/src/subtitle_format_srt.h @@ -37,14 +37,14 @@ class AssDialogue; class SRTSubtitleFormat : public SubtitleFormat { - wxString ConvertTags(const AssDialogue *diag) const; + std::string ConvertTags(const AssDialogue *diag) const; public: SRTSubtitleFormat(); - wxArrayString GetReadWildcards() const override; - wxArrayString GetWriteWildcards() const override; + std::vector GetReadWildcards() const override; + std::vector GetWriteWildcards() const override; bool CanSave(const AssFile *file) const override; - void ReadFile(AssFile *target, wxString const& filename, wxString const& forceEncoding) const override; - void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const override; + void ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& forceEncoding) const override; + void WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const override; }; diff --git a/aegisub/src/subtitle_format_transtation.cpp b/aegisub/src/subtitle_format_transtation.cpp index 732fee172..6f38f7e09 100644 --- a/aegisub/src/subtitle_format_transtation.cpp +++ b/aegisub/src/subtitle_format_transtation.cpp @@ -42,27 +42,29 @@ #include "ass_file.h" #include "ass_style.h" #include "ass_time.h" -#include "compat.h" #include "text_file_writer.h" #include +#include +#include + TranStationSubtitleFormat::TranStationSubtitleFormat() : SubtitleFormat("TranStation") { } -wxArrayString TranStationSubtitleFormat::GetWriteWildcards() const { - wxArrayString formats; - formats.Add("transtation.txt"); +std::vector TranStationSubtitleFormat::GetWriteWildcards() const { + std::vector formats; + formats.push_back("transtation.txt"); return formats; } -bool TranStationSubtitleFormat::CanWriteFile(wxString const& filename) const { - return filename.Lower().EndsWith(".transtation.txt"); +bool TranStationSubtitleFormat::CanWriteFile(agi::fs::path const& filename) const { + return boost::iends_with(filename.string(), ".transtation.txt"); } -void TranStationSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const { +void TranStationSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const { agi::vfr::Framerate fps = AskForFPS(false, true); if (!fps.IsLoaded()) return; @@ -95,11 +97,11 @@ void TranStationSubtitleFormat::WriteFile(const AssFile *src, wxString const& fi file.WriteLineToFile("SUB["); } -wxString TranStationSubtitleFormat::ConvertLine(AssFile *file, AssDialogue *current, agi::vfr::Framerate const& fps, SmpteFormatter const& ft, int nextl_start) const { +std::string TranStationSubtitleFormat::ConvertLine(AssFile *file, AssDialogue *current, agi::vfr::Framerate const& fps, SmpteFormatter const& ft, int nextl_start) const { int valign = 0; const char *halign = " "; // default is centered const char *type = "N"; // no special style - if (AssStyle *style = file->GetStyle(from_wx(current->Style))) { + if (AssStyle *style = file->GetStyle(current->Style)) { if (style->alignment >= 4) valign = 4; if (style->alignment >= 7) valign = 9; if (style->alignment == 1 || style->alignment == 4 || style->alignment == 7) halign = "L"; @@ -109,7 +111,7 @@ wxString TranStationSubtitleFormat::ConvertLine(AssFile *file, AssDialogue *curr // Hack: If an italics-tag (\i1) appears anywhere in the line, // make it all italics - if (current->Text.get().Find("\\i1") != wxNOT_FOUND) type = "I"; + if (current->Text.get().find("\\i1") != std::string::npos) type = "I"; // Write header AssTime end = current->End; @@ -120,6 +122,6 @@ wxString TranStationSubtitleFormat::ConvertLine(AssFile *file, AssDialogue *curr if (nextl_start > 0 && end == nextl_start) end = fps.TimeAtFrame(fps.FrameAtTime(end, agi::vfr::END) - 1, agi::vfr::END); - wxString header = wxString::Format("SUB[%i%s%s ", valign, halign, type) + ft.ToSMPTE(current->Start) + ">" + ft.ToSMPTE(end) + "]\r\n"; - return header + current->Text; + std::string header = str(boost::format("SUB[%i%s%s %s>%s]\r\n") % valign % halign % type % ft.ToSMPTE(current->Start) % ft.ToSMPTE(end)); + return header + current->Text.get(); } diff --git a/aegisub/src/subtitle_format_transtation.h b/aegisub/src/subtitle_format_transtation.h index f75bd9f1d..6b951e34b 100644 --- a/aegisub/src/subtitle_format_transtation.h +++ b/aegisub/src/subtitle_format_transtation.h @@ -38,11 +38,11 @@ class AssDialogue; class SmpteFormatter; class TranStationSubtitleFormat : public SubtitleFormat { - wxString ConvertLine(AssFile *file, AssDialogue *line, agi::vfr::Framerate const& fps, SmpteFormatter const& ft, int nextl_start) const; + std::string ConvertLine(AssFile *file, AssDialogue *line, agi::vfr::Framerate const& fps, SmpteFormatter const& ft, int nextl_start) const; public: TranStationSubtitleFormat(); - bool CanWriteFile(wxString const& filename) const override; - wxArrayString GetWriteWildcards() const override; - void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const override; + bool CanWriteFile(agi::fs::path const& filename) const override; + std::vector GetWriteWildcards() const override; + void WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const override; }; diff --git a/aegisub/src/subtitle_format_ttxt.cpp b/aegisub/src/subtitle_format_ttxt.cpp index ea5d41998..da2365c0e 100644 --- a/aegisub/src/subtitle_format_ttxt.cpp +++ b/aegisub/src/subtitle_format_ttxt.cpp @@ -54,22 +54,22 @@ TTXTSubtitleFormat::TTXTSubtitleFormat() { } -wxArrayString TTXTSubtitleFormat::GetReadWildcards() const { - wxArrayString formats; - formats.Add("ttxt"); +std::vector TTXTSubtitleFormat::GetReadWildcards() const { + std::vector formats; + formats.push_back("ttxt"); return formats; } -wxArrayString TTXTSubtitleFormat::GetWriteWildcards() const { +std::vector TTXTSubtitleFormat::GetWriteWildcards() const { return GetReadWildcards(); } -void TTXTSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxString const& encoding) const { +void TTXTSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& encoding) const { target->LoadDefault(false); // Load XML document wxXmlDocument doc; - if (!doc.Load(filename)) throw TTXTParseError("Failed loading TTXT XML file.", 0); + if (!doc.Load(filename.wstring())) throw TTXTParseError("Failed loading TTXT XML file.", 0); // Check root node name if (doc.GetRoot()->GetName() != "TextStream") throw TTXTParseError("Invalid TTXT file.", 0); @@ -109,7 +109,7 @@ void TTXTSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxS AssDialogue *TTXTSubtitleFormat::ProcessLine(wxXmlNode *node, AssDialogue *prev, int version) const { // Get time wxString sampleTime = node->GetAttribute("sampleTime", "00:00:00.000"); - AssTime time(sampleTime); + AssTime time(from_wx(sampleTime)); // Set end time of last line if (prev) @@ -144,14 +144,14 @@ AssDialogue *TTXTSubtitleFormat::ProcessLine(wxXmlNode *node, AssDialogue *prev, } else if (in) finalText += chr; } - diag->Text = finalText; + diag->Text = from_wx(finalText); } // Process text for 1.1 else { text.Replace("\r", ""); text.Replace("\n", "\\N"); - diag->Text = text; + diag->Text = from_wx(text); } return diag; @@ -161,7 +161,7 @@ void TTXTSubtitleFormat::ProcessHeader(wxXmlNode *node) const { // TODO } -void TTXTSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const { +void TTXTSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const { // Convert to TTXT AssFile copy(*src); ConvertToTTXT(copy); @@ -183,7 +183,7 @@ void TTXTSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, } // Save XML - doc.Save(filename); + doc.Save(filename.wstring()); } void TTXTSubtitleFormat::WriteHeader(wxXmlNode *root) const { @@ -240,7 +240,7 @@ void TTXTSubtitleFormat::WriteLine(wxXmlNode *root, const AssDialogue *prev, con // If it doesn't start at the end of previous, add blank if (prev && prev->End != line->Start) { wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextSample"); - node->AddAttribute("sampleTime", "0" + prev->End.GetAssFormated(true)); + node->AddAttribute("sampleTime", to_wx("0" + prev->End.GetAssFormated(true))); node->AddAttribute("xml:space", "preserve"); root->AddChild(node); node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", "")); @@ -248,10 +248,10 @@ void TTXTSubtitleFormat::WriteLine(wxXmlNode *root, const AssDialogue *prev, con // Generate and insert node wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextSample"); - node->AddAttribute("sampleTime", "0" + line->Start.GetAssFormated(true)); + node->AddAttribute("sampleTime", to_wx("0" + line->Start.GetAssFormated(true))); node->AddAttribute("xml:space", "preserve"); root->AddChild(node); - node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", line->Text)); + node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", to_wx(line->Text))); } void TTXTSubtitleFormat::ConvertToTTXT(AssFile &file) const { diff --git a/aegisub/src/subtitle_format_ttxt.h b/aegisub/src/subtitle_format_ttxt.h index fc57744ad..111a87b18 100644 --- a/aegisub/src/subtitle_format_ttxt.h +++ b/aegisub/src/subtitle_format_ttxt.h @@ -48,9 +48,9 @@ class TTXTSubtitleFormat : public SubtitleFormat { public: TTXTSubtitleFormat(); - wxArrayString GetReadWildcards() const override; - wxArrayString GetWriteWildcards() const override; + std::vector GetReadWildcards() const override; + std::vector GetWriteWildcards() const override; - void ReadFile(AssFile *target, wxString const& filename, wxString const& forceEncoding) const override; - void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const override; + void ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& forceEncoding) const override; + void WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const override; }; diff --git a/aegisub/src/subtitle_format_txt.cpp b/aegisub/src/subtitle_format_txt.cpp index 2966a0e66..69bed006d 100644 --- a/aegisub/src/subtitle_format_txt.cpp +++ b/aegisub/src/subtitle_format_txt.cpp @@ -38,7 +38,6 @@ #include "ass_dialogue.h" #include "ass_file.h" -#include "compat.h" #include "dialog_text_import.h" #include "options.h" #include "text_file_reader.h" @@ -48,27 +47,30 @@ #include +#include +#include + TXTSubtitleFormat::TXTSubtitleFormat() : SubtitleFormat("Plain-Text") { } -wxArrayString TXTSubtitleFormat::GetReadWildcards() const { - wxArrayString formats; - formats.Add("txt"); +std::vector TXTSubtitleFormat::GetReadWildcards() const { + std::vector formats; + formats.push_back("txt"); return formats; } -wxArrayString TXTSubtitleFormat::GetWriteWildcards() const { +std::vector TXTSubtitleFormat::GetWriteWildcards() const { return GetReadWildcards(); } -bool TXTSubtitleFormat::CanWriteFile(wxString const& filename) const { - return (filename.Right(4).Lower() == ".txt" && filename.Right(11).Lower() != ".encore.txt" && filename.Right(16).Lower() != ".transtation.txt"); +bool TXTSubtitleFormat::CanWriteFile(agi::fs::path const& filename) const { + auto str = filename.string(); + return boost::iends_with(str, ".txt") && !(boost::iends_with(str, ".encore.txt") || boost::iends_with(str, ".transtation.txt")); } -void TXTSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxString const& encoding) const { - using namespace std; +void TXTSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& encoding) const { DialogTextImport dlg; if (dlg.ShowModal() == wxID_CANCEL) return; @@ -76,58 +78,56 @@ void TXTSubtitleFormat::ReadFile(AssFile *target, wxString const& filename, wxSt target->LoadDefault(false); - wxString actor; - wxString separator = to_wx(OPT_GET("Tool/Import/Text/Actor Separator")->GetString()); - wxString comment = to_wx(OPT_GET("Tool/Import/Text/Comment Starter")->GetString()); + std::string actor; + std::string separator = OPT_GET("Tool/Import/Text/Actor Separator")->GetString(); + std::string comment = OPT_GET("Tool/Import/Text/Comment Starter")->GetString(); // Parse file while (file.HasMoreLines()) { - wxString value = file.ReadLineFromFile(); + std::string value = file.ReadLineFromFile(); if(value.empty()) continue; // Check if this isn't a timecodes file - if (value.StartsWith("# timecode")) + if (boost::starts_with(value, "# timecode")) throw SubtitleFormatParseError("File is a timecode file, cannot load as subtitles.", 0); // Read comment data bool isComment = false; - if (!comment.empty() && value.StartsWith(comment)) { + if (!comment.empty() && boost::starts_with(value, comment)) { isComment = true; - value = value.Mid(comment.size()); + value.erase(0, comment.size()); } // Read actor data if (!isComment && !separator.empty()) { if (value[0] != ' ' && value[0] != '\t') { - int pos = value.Find(separator); - if (pos != wxNOT_FOUND) { - actor = value.Left(pos); - actor.Trim(false); - actor.Trim(true); - value = value.Mid(pos+1); + size_t pos = value.find(separator); + if (pos != std::string::npos) { + actor = value.substr(0, pos); + boost::trim(actor); + value.erase(0, pos); } } } // Trim spaces at start - value.Trim(false); + boost::trim_left(value); if (value.empty()) isComment = true; // Sets line up AssDialogue *line = new AssDialogue; - line->Actor = isComment ? wxString() : actor; + line->Actor = isComment ? std::string() : actor; line->Comment = isComment; line->Text = value; line->End = 0; - // Adds line target->Line.push_back(*line); } } -void TXTSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const { +void TXTSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const { size_t num_actor_names = 0, num_dialogue_lines = 0; // Detect number of lines with Actor field filled out @@ -144,19 +144,19 @@ void TXTSubtitleFormat::WriteFile(const AssFile *src, wxString const& filename, bool strip_formatting = true; TextFileWriter file(filename, encoding); - file.WriteLineToFile(wxString("# Exported by Aegisub ") + GetAegisubShortVersionString()); + file.WriteLineToFile(std::string("# Exported by Aegisub ") + GetAegisubShortVersionString()); // Write the file for (auto dia : src->Line | agi::of_type()) { - wxString out_line; + std::string out_line; if (dia->Comment) out_line = "# "; if (write_actors) - out_line += dia->Actor + ": "; + out_line += dia->Actor.get() + ": "; - wxString out_text = strip_formatting ? dia->GetStrippedText() : dia->Text; + std::string out_text = strip_formatting ? dia->GetStrippedText() : dia->Text; out_line += out_text; if (!out_text.empty()) diff --git a/aegisub/src/subtitle_format_txt.h b/aegisub/src/subtitle_format_txt.h index 6a9b7d1ef..0c6c10359 100644 --- a/aegisub/src/subtitle_format_txt.h +++ b/aegisub/src/subtitle_format_txt.h @@ -37,13 +37,13 @@ class TXTSubtitleFormat : public SubtitleFormat { public: TXTSubtitleFormat(); - wxArrayString GetReadWildcards() const override; - wxArrayString GetWriteWildcards() const override; + std::vector GetReadWildcards() const override; + std::vector GetWriteWildcards() const override; // TXT format supports so little that it should always require an export bool CanSave(const AssFile*) const override { return false; } - bool CanWriteFile(wxString const& filename) const override; - void ReadFile(AssFile *target, wxString const& filename, wxString const& forceEncoding) const override; - void WriteFile(const AssFile *src, wxString const& filename, wxString const& encoding) const override; + bool CanWriteFile(agi::fs::path const& filename) const override; + void ReadFile(AssFile *target, agi::fs::path const& filename, std::string const& forceEncoding) const override; + void WriteFile(const AssFile *src, agi::fs::path const& filename, std::string const& encoding) const override; }; diff --git a/aegisub/src/subtitles_provider.cpp b/aegisub/src/subtitles_provider.cpp index 163451433..8bd313739 100644 --- a/aegisub/src/subtitles_provider.cpp +++ b/aegisub/src/subtitles_provider.cpp @@ -34,24 +34,16 @@ #include "config.h" -#include "compat.h" #include "options.h" -#ifdef WITH_CSRI #include "subtitles_provider_csri.h" -#endif -#ifdef WITH_LIBASS #include "subtitles_provider_libass.h" -#endif -#if !defined(WITH_CSRI) && !defined(WITH_LIBASS) #include "include/aegisub/subtitles_provider.h" -#endif SubtitlesProvider* SubtitlesProviderFactory::GetProvider() { std::vector list = GetClasses(OPT_GET("Subtitle/Provider")->GetString()); - if (list.empty()) throw wxString("No subtitle providers are available."); + if (list.empty()) throw std::string("No subtitle providers are available."); - // Get provider - wxString error; + std::string error; for (auto const& factory : list) { try { size_t pos = factory.find('/'); @@ -60,8 +52,8 @@ SubtitlesProvider* SubtitlesProviderFactory::GetProvider() { if (provider) return provider; } catch (agi::UserCancelException const&) { throw; } - catch (wxString const& err) { error += factory + " factory: " + err + "\n"; } - catch (const char *err) { error += factory + " factory: " + wxString(err) + "\n"; } + catch (std::string const& err) { error += factory + " factory: " + err + "\n"; } + catch (const char *err) { error += factory + " factory: " + std::string(err) + "\n"; } catch (...) { error += factory + " factory: Unknown error\n"; } } diff --git a/aegisub/src/subtitles_provider_csri.cpp b/aegisub/src/subtitles_provider_csri.cpp index e6c6a44be..90c557b12 100644 --- a/aegisub/src/subtitles_provider_csri.cpp +++ b/aegisub/src/subtitles_provider_csri.cpp @@ -35,16 +35,18 @@ #include "config.h" #ifdef WITH_CSRI - -#include - #include "subtitles_provider_csri.h" #include "ass_file.h" -#include "text_file_writer.h" +#include "standard_paths.h" #include "video_context.h" #include "video_frame.h" +#include + +#include +#include + #ifdef WIN32 #define CSRIAPI #include "../../contrib/csri/include/csri/csri.h" @@ -52,13 +54,15 @@ #include #endif -static wxMutex csri_mutex; +// CSRI renderers are not required to be thread safe (and VSFilter very much +// is not) +static std::mutex csri_mutex; CSRISubtitlesProvider::CSRISubtitlesProvider(std::string type) : can_open_mem(true) -, instance(0, csri_close) +, instance(nullptr, csri_close) { - wxMutexLocker lock(csri_mutex); + std::lock_guard lock(csri_mutex); for (csri_rend *cur = csri_renderer_default(); cur; cur = csri_renderer_next(cur)) { if (type == csri_renderer_info(cur)->name) { @@ -75,7 +79,7 @@ CSRISubtitlesProvider::CSRISubtitlesProvider(std::string type) } CSRISubtitlesProvider::~CSRISubtitlesProvider() { - if (!tempfile.empty()) wxRemoveFile(tempfile); + agi::fs::Remove(tempfile); } void CSRISubtitlesProvider::LoadSubtitles(AssFile *subs) { @@ -83,28 +87,22 @@ void CSRISubtitlesProvider::LoadSubtitles(AssFile *subs) { std::vector data; subs->SaveMemory(data); - wxMutexLocker lock(csri_mutex); + std::lock_guard lock(csri_mutex); instance = csri_open_mem(renderer, &data[0], data.size(), nullptr); } - // Open from disk else { - if (tempfile.empty()) { - tempfile = wxFileName::CreateTempFileName("aegisub"); - wxRemoveFile(tempfile); - tempfile += ".ass"; - } - subs->Save(tempfile, false, false, wxSTRING_ENCODING); + if (tempfile.empty()) + tempfile = unique_path(StandardPaths::DecodePath("?temp/csri-%%%%-%%%%-%%%%-%%%%.ass")); + subs->Save(tempfile, false, false, "utf-8"); - wxMutexLocker lock(csri_mutex); - instance = csri_open_file(renderer, tempfile.utf8_str(), nullptr); + std::lock_guard lock(csri_mutex); + instance = csri_open_file(renderer, tempfile.string().c_str(), nullptr); } } void CSRISubtitlesProvider::DrawSubtitles(AegiVideoFrame &dst,double time) { - // Check if CSRI loaded properly if (!instance) return; - // Load data into frame csri_frame frame; if (dst.flipped) { frame.planes[0] = dst.data + (dst.h-1) * dst.pitch; @@ -116,18 +114,11 @@ void CSRISubtitlesProvider::DrawSubtitles(AegiVideoFrame &dst,double time) { } frame.pixfmt = CSRI_F_BGR_; - // Set format - csri_fmt format; - format.width = dst.w; - format.height = dst.h; - format.pixfmt = frame.pixfmt; + csri_fmt format = { frame.pixfmt, dst.w, dst.h }; - wxMutexLocker lock(csri_mutex); - int error = csri_request_fmt(instance, &format); - if (error) return; - - // Render - csri_render(instance, &frame, time); + std::lock_guard lock(csri_mutex); + if (!csri_request_fmt(instance, &format)) + csri_render(instance, &frame, time); } std::vector CSRISubtitlesProvider::GetSubTypes() { diff --git a/aegisub/src/subtitles_provider_csri.h b/aegisub/src/subtitles_provider_csri.h index 6f460a670..588f6d2cd 100644 --- a/aegisub/src/subtitles_provider_csri.h +++ b/aegisub/src/subtitles_provider_csri.h @@ -33,13 +33,15 @@ /// #ifdef WITH_CSRI - #include "include/aegisub/subtitles_provider.h" #include +#include #include +#include + typedef void csri_rend; typedef void csri_inst; @@ -53,13 +55,13 @@ class CSRISubtitlesProvider : public SubtitlesProvider { bool can_open_mem; /// Name of the file passed to renderers with can_open_mem false - wxString tempfile; + agi::fs::path tempfile; public: CSRISubtitlesProvider(std::string subType); ~CSRISubtitlesProvider(); void LoadSubtitles(AssFile *subs); - void DrawSubtitles(AegiVideoFrame &dst,double time); + void DrawSubtitles(AegiVideoFrame &dst, double time); static std::vector GetSubTypes(); }; diff --git a/aegisub/src/subtitles_provider_libass.cpp b/aegisub/src/subtitles_provider_libass.cpp index 69fb43f65..8daef6655 100644 --- a/aegisub/src/subtitles_provider_libass.cpp +++ b/aegisub/src/subtitles_provider_libass.cpp @@ -35,28 +35,33 @@ #include "config.h" #ifdef WITH_LIBASS - -#include -#include +#include "subtitles_provider_libass.h" #ifdef __APPLE__ #include #include #endif -#include - #include "ass_file.h" #include "dialog_progress.h" #include "frame_main.h" #include "main.h" -#include "standard_paths.h" -#include "subtitles_provider_libass.h" #include "utils.h" -#include "video_context.h" #include "video_frame.h" -static void msg_callback(int level, const char *fmt, va_list args, void *) { +#include +#include + +#include +#include +#include +#include + +namespace { +std::unique_ptr cache_queue; +ASS_Library *library; + +void msg_callback(int level, const char *fmt, va_list args, void *) { if (level >= 7) return; char buf[1024]; #ifdef _WIN32 @@ -71,65 +76,38 @@ static void msg_callback(int level, const char *fmt, va_list args, void *) { LOG_D("subtitle/provider/libass") << buf; } -class FontConfigCacheThread : public wxThread { - ASS_Library *ass_library; - ASS_Renderer *ass_renderer; - FontConfigCacheThread** thisPtr; - ExitCode Entry() { - const char *config_path = nullptr; #ifdef __APPLE__ - std::string conf_path = agi::util::OSX_GetBundleResourcesDirectory() + "/etc/fonts/fonts.conf"; - config_path = conf_path.c_str(); +#define CONFIG_PATH (agi::util::OSX_GetBundleResourcesDirectory() + "/etc/fonts/fonts.conf").c_str() +#else +#define CONFIG_PATH nullptr #endif - - if (ass_library) ass_renderer = ass_renderer_init(ass_library); - ass_set_fonts(ass_renderer, nullptr, "Sans", 1, config_path, true); - if (ass_library) ass_renderer_done(ass_renderer); - *thisPtr = nullptr; - return EXIT_SUCCESS; - } -public: - FontConfigCacheThread(ASS_Library *ass_library, FontConfigCacheThread **thisPtr) - : ass_library(ass_library) - , ass_renderer(nullptr) - , thisPtr(thisPtr) - { - *thisPtr = this; - Create(); - Run(); - } - FontConfigCacheThread(ASS_Renderer *ass_renderer, FontConfigCacheThread **thisPtr) - : ass_library(nullptr) - , ass_renderer(ass_renderer) - , thisPtr(thisPtr) - { - *thisPtr = this; - Create(); - Run(); - } -}; - -static void wait_for_cache_thread(FontConfigCacheThread const * const * const cache_worker) { - if (!*cache_worker) return; - - DialogProgress progress(wxGetApp().frame, "Updating font index", "This may take several minutes"); - progress.Run([=](agi::ProgressSink *ps) { - ps->SetIndeterminate(); - while (*cache_worker && !ps->IsCancelled()) - wxMilliSleep(100); - }); } -LibassSubtitlesProvider::LibassSubtitlesProvider(std::string) { - wait_for_cache_thread(&cache_worker); +LibassSubtitlesProvider::LibassSubtitlesProvider(std::string) +: ass_renderer(nullptr) +, ass_track(nullptr) +{ + auto done = std::make_shared(false); + auto renderer = std::make_shared(nullptr); + cache_queue->Async([=]{ + auto ass_renderer = ass_renderer_init(library); + if (ass_renderer) { + ass_set_font_scale(ass_renderer, 1.); + ass_set_fonts(ass_renderer, nullptr, "Sans", 1, CONFIG_PATH, true); + } + *done = true; + *renderer = ass_renderer; + }); - // Initialize renderer - ass_track = nullptr; - ass_renderer = ass_renderer_init(ass_library); + DialogProgress progress(wxGetApp().frame, _("Updating font index"), _("This may take several minutes")); + progress.Run([=](agi::ProgressSink *ps) { + ps->SetIndeterminate(); + while (!*done && !ps->IsCancelled()) + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + }); + + ass_renderer = *renderer; if (!ass_renderer) throw "ass_renderer_init failed"; - ass_set_font_scale(ass_renderer, 1.); - new FontConfigCacheThread(ass_renderer, &cache_worker); - wait_for_cache_thread(&cache_worker); } LibassSubtitlesProvider::~LibassSubtitlesProvider() { @@ -138,37 +116,32 @@ LibassSubtitlesProvider::~LibassSubtitlesProvider() { } void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) { - // Prepare subtitles std::vector data; subs->SaveMemory(data); - // Load file if (ass_track) ass_free_track(ass_track); - ass_track = ass_read_memory(ass_library, &data[0], data.size(),(char *)"UTF-8"); + ass_track = ass_read_memory(library, &data[0], data.size(),(char *)"UTF-8"); if (!ass_track) throw "libass failed to load subtitles."; } -#define _r(c) ((c)>>24) -#define _g(c) (((c)>>16)&0xFF) -#define _b(c) (((c)>>8)&0xFF) -#define _a(c) ((c)&0xFF) +#define _r(c) ((c)>>24) +#define _g(c) (((c)>>16)&0xFF) +#define _b(c) (((c)>>8)&0xFF) +#define _a(c) ((c)&0xFF) void LibassSubtitlesProvider::DrawSubtitles(AegiVideoFrame &frame,double time) { - // Set size ass_set_frame_size(ass_renderer, frame.w, frame.h); - // Get frame ASS_Image* img = ass_render_frame(ass_renderer, ass_track, int(time * 1000), nullptr); // libass actually returns several alpha-masked monochrome images. // Here, we loop through their linked list, get the colour of the current, and blend into the frame. // This is repeated for all of them. while (img) { - // Get colours unsigned int opacity = 255 - ((unsigned int)_a(img->color)); unsigned int r = (unsigned int)_r(img->color); unsigned int g = (unsigned int)_g(img->color); - unsigned int b = (unsigned int) _b(img->color); + unsigned int b = (unsigned int)_b(img->color); // Prepare copy int src_stride = img->stride; @@ -206,18 +179,24 @@ void LibassSubtitlesProvider::DrawSubtitles(AegiVideoFrame &frame,double time) { src += src_stride; } - // Next image img = img->next; } } void LibassSubtitlesProvider::CacheFonts() { - ass_library = ass_library_init(); - ass_set_message_cb(ass_library, msg_callback, nullptr); - new FontConfigCacheThread(ass_library, &cache_worker); + // Initialize the cache worker thread + cache_queue = agi::dispatch::Create(); + + // Initialize libass + library = ass_library_init(); + ass_set_message_cb(library, msg_callback, nullptr); + + // Initialize a renderer to force fontconfig to update its cache + cache_queue->Async([]{ + auto ass_renderer = ass_renderer_init(library); + ass_set_fonts(ass_renderer, nullptr, "Sans", 1, CONFIG_PATH, true); + ass_renderer_done(ass_renderer); + }); } -ASS_Library* LibassSubtitlesProvider::ass_library; -FontConfigCacheThread* LibassSubtitlesProvider::cache_worker = nullptr; - #endif // WITH_LIBASS diff --git a/aegisub/src/subtitles_provider_libass.h b/aegisub/src/subtitles_provider_libass.h index 8e22200b2..84ee24c12 100644 --- a/aegisub/src/subtitles_provider_libass.h +++ b/aegisub/src/subtitles_provider_libass.h @@ -33,31 +33,22 @@ /// #ifdef WITH_LIBASS - #include "include/aegisub/subtitles_provider.h" -extern "C" { -#ifdef __VISUALC__ -#include "cstdint" -#endif +extern "C" { #include } -class FontConfigCacheThread; - class LibassSubtitlesProvider : public SubtitlesProvider { - static ASS_Library* ass_library; ASS_Renderer* ass_renderer; ASS_Track* ass_track; - static FontConfigCacheThread *cache_worker; - public: LibassSubtitlesProvider(std::string); ~LibassSubtitlesProvider(); void LoadSubtitles(AssFile *subs); - void DrawSubtitles(AegiVideoFrame &dst,double time); + void DrawSubtitles(AegiVideoFrame &dst, double time); static void CacheFonts(); }; diff --git a/aegisub/src/text_file_reader.cpp b/aegisub/src/text_file_reader.cpp index ad8ffccdb..5b5d61e5c 100644 --- a/aegisub/src/text_file_reader.cpp +++ b/aegisub/src/text_file_reader.cpp @@ -21,52 +21,34 @@ #include "config.h" -#include -#include -#include -#include - -#include - -#include -#include - -#include "charset_detect.h" -#include "compat.h" #include "text_file_reader.h" -TextFileReader::TextFileReader(wxString const& filename, wxString encoding, bool trim) +#include "charset_detect.h" + +#include + +#include +#include +#include + +TextFileReader::TextFileReader(agi::fs::path const& filename, std::string encoding, bool trim) : trim(trim) { - if (encoding.empty()) encoding = CharSetDetect::GetEncoding(filename); - file.reset(agi::io::Open(from_wx(filename), true)); - iter = agi::line_iterator(*file, from_wx(encoding)); + if (encoding.empty()) + encoding = CharSetDetect::GetEncoding(filename); + file.reset(agi::io::Open(filename, true)); + iter = agi::line_iterator(*file, encoding); } TextFileReader::~TextFileReader() { } -wxString TextFileReader::ReadLineFromFile() { - wxString str = *iter; +std::string TextFileReader::ReadLineFromFile() { + std::string str = *iter; ++iter; - if (trim) str.Trim(true).Trim(false); - if (str.StartsWith(L"\uFEFF")) str = str.Mid(1); + if (trim) + boost::trim(str); + if (boost::starts_with(str, "\xEF\xBB\xBF")) + str.erase(0, 3); return str; } - -namespace agi { -#ifdef _WIN32 - template<> void line_iterator::init() { - conv.reset(new agi::charset::IconvWrapper(encoding.c_str(), "utf-16le")); - } - template<> bool line_iterator::convert(std::string &str) { - value = wxString(str.c_str(), wxMBConvUTF16LE(), str.size()); - return true; - } -#else - template<> bool line_iterator::convert(std::string &str) { - value = str; - return true; - } -#endif -} diff --git a/aegisub/src/text_file_reader.h b/aegisub/src/text_file_reader.h index 68cffc9e4..9b54cf3cf 100644 --- a/aegisub/src/text_file_reader.h +++ b/aegisub/src/text_file_reader.h @@ -23,10 +23,9 @@ #include #include +#include -#include -#include - +#include #include /// @class TextFileReader @@ -34,7 +33,7 @@ class TextFileReader { std::unique_ptr file; bool trim; - agi::line_iterator iter; + agi::line_iterator iter; TextFileReader(const TextFileReader&); TextFileReader& operator=(const TextFileReader&); @@ -44,13 +43,13 @@ public: /// @param filename File to open /// @param enc Encoding to use, or empty to autodetect /// @param trim Whether to trim whitespace from lines read - TextFileReader(wxString const& filename,wxString encoding="", bool trim=true); + TextFileReader(agi::fs::path const& filename, std::string encoding="", bool trim=true); /// @brief Destructor ~TextFileReader(); /// @brief Read a line from the file /// @return The line, possibly trimmed - wxString ReadLineFromFile(); + std::string ReadLineFromFile(); /// @brief Check if there are any more lines to read - bool HasMoreLines() const { return iter != agi::line_iterator(); } + bool HasMoreLines() const { return iter != agi::line_iterator(); } }; diff --git a/aegisub/src/text_file_writer.cpp b/aegisub/src/text_file_writer.cpp index 98a95b04d..57e6179e8 100644 --- a/aegisub/src/text_file_writer.cpp +++ b/aegisub/src/text_file_writer.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2005, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2013, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. +// 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/ @@ -34,26 +21,28 @@ #include "config.h" -#include - -#include - -#include "charset_conv.h" -#include "compat.h" -#include "options.h" #include "text_file_writer.h" -TextFileWriter::TextFileWriter(wxString const& filename, wxString encoding) -: file(new agi::io::Save(from_wx(filename), true)) +#include "options.h" + +#include +#include + +#include +#include + +TextFileWriter::TextFileWriter(agi::fs::path const& filename, std::string encoding) +: file(new agi::io::Save(filename, true)) , conv() { - if (encoding.empty()) encoding = to_wx(OPT_GET("App/Save Charset")->GetString()); - if (encoding.Lower() != wxSTRING_ENCODING) - conv.reset(new agi::charset::IconvWrapper(wxSTRING_ENCODING, encoding.utf8_str(), true)); + if (encoding.empty()) + encoding = OPT_GET("App/Save Charset")->GetString(); + if (boost::iequals(encoding, "utf-8")) + conv.reset(new agi::charset::IconvWrapper("utf-8", encoding.c_str(), true)); - // Write the BOM try { - WriteLineToFile(L"\uFEFF", false); + // Write the BOM + WriteLineToFile("\xEF\xBB\xBF", false); } catch (agi::charset::ConversionFailure&) { // If the BOM could not be converted to the target encoding it isn't needed @@ -61,29 +50,18 @@ TextFileWriter::TextFileWriter(wxString const& filename, wxString encoding) } TextFileWriter::~TextFileWriter() { - // Explicit empty destructor required with an auto_ptr to an incomplete class + // Explicit empty destructor required with a unique_ptr to an incomplete class } -void TextFileWriter::WriteLineToFile(wxString line, bool addLineBreak) { +void TextFileWriter::WriteLineToFile(std::string&& line, bool addLineBreak) { + if (addLineBreak) #ifdef _WIN32 - if (addLineBreak) line += "\r\n"; + line += "\r\n"; #else - if (addLineBreak) line += "\n"; + line += "\n"; #endif - // On non-windows this cast does nothing - const char *data = reinterpret_cast(line.wx_str()); -#if wxUSE_UNICODE_UTF8 - size_t len = line.utf8_length(); -#else - size_t len = line.length() * sizeof(wxStringCharType); -#endif - - if (conv) { - std::string buf = conv->Convert(std::string(data, len)); - file->Get().write(buf.data(), buf.size()); - } - else { - file->Get().write(data, len); - } + if (conv) + line = conv->Convert(line); + file->Get().write(line.data(), line.size()); } diff --git a/aegisub/src/text_file_writer.h b/aegisub/src/text_file_writer.h index 7282d052d..e97c3770d 100644 --- a/aegisub/src/text_file_writer.h +++ b/aegisub/src/text_file_writer.h @@ -1,29 +1,16 @@ -// Copyright (c) 2005, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2013, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. +// 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/ @@ -32,34 +19,24 @@ /// @ingroup utility /// +#include +#include - -#include - -#include +#include namespace agi { namespace charset { class IconvWrapper; } namespace io { class Save; } } -#include - class TextFileWriter { - agi::scoped_ptr file; - agi::scoped_ptr conv; + std::unique_ptr file; + std::unique_ptr conv; public: - TextFileWriter(wxString const& filename, wxString encoding=""); + TextFileWriter(agi::fs::path const& filename, std::string encoding=""); ~TextFileWriter(); - void WriteLineToFile(wxString line, bool addLineBreak=true); + void WriteLineToFile(std::string const& line, bool addLineBreak=true) { WriteLineToFile(std::string(line), addLineBreak); } + void WriteLineToFile(std::string&& line, bool addLineBreak=true); }; - -#if wxUSE_UNICODE_UTF8 -#define wxSTRING_ENCODING "utf-8" -#elif defined(_WIN32) -#define wxSTRING_ENCODING "utf-16le" -#else -#define wxSTRING_ENCODING "utf-32le" -#endif diff --git a/aegisub/src/thesaurus.cpp b/aegisub/src/thesaurus.cpp index 4607d71f0..7cca9b3b3 100644 --- a/aegisub/src/thesaurus.cpp +++ b/aegisub/src/thesaurus.cpp @@ -23,17 +23,16 @@ #include "thesaurus.h" -#include -#include - -#include -#include -#include - -#include "compat.h" #include "options.h" #include "standard_paths.h" +#include +#include + +#include +#include +#include + Thesaurus::Thesaurus() : lang_listener(OPT_SUB("Tool/Thesaurus/Language", &Thesaurus::OnLanguageChanged, this)) , dict_path_listener(OPT_SUB("Path/Dictionary", &Thesaurus::OnPathChanged, this)) @@ -47,46 +46,43 @@ Thesaurus::~Thesaurus() { void Thesaurus::Lookup(std::string word, std::vector *result) { if (!impl.get()) return; - agi::util::str_lower(word); + boost::to_lower(word); impl->Lookup(word, result); } std::vector Thesaurus::GetLanguageList() const { if (!languages.empty()) return languages; - wxArrayString idx, dat; + std::vector idx, dat; // Get list of dictionaries - wxString path = StandardPaths::DecodePath("?data/dictionaries/"); - if (wxFileName::DirExists(path)) { - wxDir::GetAllFiles(path, &idx, "th_*.idx", wxDIR_FILES); - wxDir::GetAllFiles(path, &dat, "th_*.dat", wxDIR_FILES); - } - path = StandardPaths::DecodePath(to_wx(OPT_GET("Path/Dictionary")->GetString()) + "/"); - if (wxFileName::DirExists(path)) { - wxDir::GetAllFiles(path, &idx, "th_*.idx", wxDIR_FILES); - wxDir::GetAllFiles(path, &dat, "th_*.dat", wxDIR_FILES); - } + auto path = StandardPaths::DecodePath("?data/dictionaries/"); + agi::fs::DirectoryIterator(path, "th_*.idx").GetAll(idx); + agi::fs::DirectoryIterator(path, "th_*.dat").GetAll(dat); + + path = StandardPaths::DecodePath(OPT_GET("Path/Dictionary")->GetString()); + agi::fs::DirectoryIterator(path, "th_*.idx").GetAll(idx); + agi::fs::DirectoryIterator(path, "th_*.dat").GetAll(dat); + if (idx.empty() || dat.empty()) return languages; - idx.Sort(); - dat.Sort(); + sort(begin(idx), end(idx)); + sort(begin(dat), end(dat)); // Drop extensions and the th_ prefix - for (auto& fn : idx) fn = fn.Mid(3, fn.size() - 7); - for (auto& fn : dat) fn = fn.Mid(3, fn.size() - 7); + for (auto& fn : idx) fn = fn.substr(3, fn.size() - 7); + for (auto& fn : dat) fn = fn.substr(3, fn.size() - 7); // Verify that each idx has a dat for (size_t i = 0, j = 0; i < idx.size() && j < dat.size(); ) { - int cmp = idx[i].Cmp(dat[j]); + int cmp = idx[i].compare(dat[j]); if (cmp < 0) ++i; else if (cmp > 0) ++j; else { // Don't insert a language twice if it's in both the user dir and // the app's dir - std::string name = from_wx(wxFileName(dat[j]).GetName().Mid(3)); - if (languages.empty() || name != languages.back()) - languages.push_back(name); + if (languages.empty() || dat[j] != languages.back()) + languages.push_back(dat[j]); ++i; ++j; } @@ -97,27 +93,27 @@ std::vector Thesaurus::GetLanguageList() const { void Thesaurus::OnLanguageChanged() { impl.reset(); - std::string language = OPT_GET("Tool/Thesaurus/Language")->GetString(); + auto language = OPT_GET("Tool/Thesaurus/Language")->GetString(); if (language.empty()) return; - wxString path = StandardPaths::DecodePath(to_wx(OPT_GET("Path/Dictionary")->GetString()) + "/"); + auto path = StandardPaths::DecodePath(OPT_GET("Path/Dictionary")->GetString() + "/"); // Get index and data paths - wxString idxpath = wxString::Format("%s/th_%s.idx", path, language); - wxString datpath = wxString::Format("%s/th_%s.dat", path, language); + auto idxpath = path/str(boost::format("th_%s.idx") % language); + auto datpath = path/str(boost::format("th_%s.dat") % language); // If they aren't in the user dictionary path, check the application directory - if (!wxFileExists(idxpath) || !wxFileExists(datpath)) { + if (!agi::fs::FileExists(idxpath) || !agi::fs::FileExists(datpath)) { path = StandardPaths::DecodePath("?data/dictionaries/"); - idxpath = wxString::Format("%s/th_%s.idx", path, language); - datpath = wxString::Format("%s/th_%s.dat", path, language); + idxpath = path/str(boost::format("th_%s.idx") % language); + datpath = path/str(boost::format("th_%s.dat") % language); - if (!wxFileExists(idxpath) || !wxFileExists(datpath)) return; + if (!agi::fs::FileExists(idxpath) || !agi::fs::FileExists(datpath)) return; } - LOG_I("thesaurus/file") << "Using thesaurus: " << datpath.c_str(); + LOG_I("thesaurus/file") << "Using thesaurus: " << datpath; - impl.reset(new agi::Thesaurus(from_wx(datpath), from_wx(idxpath))); + impl.reset(new agi::Thesaurus(datpath, idxpath)); } void Thesaurus::OnPathChanged() { diff --git a/aegisub/src/thesaurus.h b/aegisub/src/thesaurus.h index f5c0bf7fe..84dc91cee 100644 --- a/aegisub/src/thesaurus.h +++ b/aegisub/src/thesaurus.h @@ -19,10 +19,10 @@ /// @ingroup thesaurus /// +#include #include #include -#include #include namespace agi { class Thesaurus; } @@ -30,8 +30,8 @@ namespace agi { class Thesaurus; } /// @class Thesaurus /// @brief A wrapper around agi::Thesarus adding wx and Aegisub-specific stuff class Thesaurus { - /// The actual thesarus implementation - agi::scoped_ptr impl; + /// The actual thesaurus implementation + std::unique_ptr impl; /// A cached list of languages available mutable std::vector languages; diff --git a/aegisub/src/threaded_frame_source.cpp b/aegisub/src/threaded_frame_source.cpp index a17eb8467..9d856423e 100644 --- a/aegisub/src/threaded_frame_source.cpp +++ b/aegisub/src/threaded_frame_source.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012, Thomas Goyne +// Copyright (c) 2013, Thomas Goyne // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -21,224 +21,188 @@ #include "threaded_frame_source.h" -#include -#include - -#include -#include - #include "ass_dialogue.h" #include "ass_file.h" -#include "compat.h" #include "export_fixstyle.h" -#include "include/aegisub/context.h" #include "include/aegisub/subtitles_provider.h" #include "video_frame.h" #include "video_provider_manager.h" +#include + +#include +#include +#include +#include +#include + enum { NEW_SUBS_FILE = -1, SUBS_FILE_ALREADY_LOADED = -2 }; -// Test if a line is a dialogue line which is not visible at the given time -struct invisible_line : public std::unary_function { - double time; - invisible_line(double time) : time(time * 1000.) { } - bool operator()(AssEntry const& entry) const { - const AssDialogue *diag = dynamic_cast(&entry); - return diag && (diag->Start > time || diag->End <= time); - } -}; - -std::shared_ptr ThreadedFrameSource::ProcFrame(int frameNum, double time, bool raw) { +std::shared_ptr ThreadedFrameSource::ProcFrame(int frame_number, double time, bool raw) { std::shared_ptr frame(new AegiVideoFrame, [](AegiVideoFrame *frame) { frame->Clear(); delete frame; }); - { - wxMutexLocker locker(providerMutex); - try { - frame->CopyFrom(videoProvider->GetFrame(frameNum)); - } - catch (VideoProviderError const& err) { throw VideoProviderErrorEvent(err); } + try { + frame->CopyFrom(video_provider->GetFrame(frame_number)); } + catch (VideoProviderError const& err) { throw VideoProviderErrorEvent(err); } - // This deliberately results in a call to LoadSubtitles while a render - // is pending making the queued render use the new file - if (!raw && provider) { - try { - wxMutexLocker locker(fileMutex); - if (subs && singleFrame != frameNum && singleFrame != SUBS_FILE_ALREADY_LOADED) { - // Generally edits and seeks come in groups; if the last thing done - // was seek it is more likely that the user will seek again and - // vice versa. As such, if this is the first frame requested after - // an edit, only export the currently visible lines (because the - // other lines will probably not be viewed before the file changes - // again), and if it's a different frame, export the entire file. - if (singleFrame != NEW_SUBS_FILE) { - provider->LoadSubtitles(subs.get()); - singleFrame = SUBS_FILE_ALREADY_LOADED; + if (raw || !subs_provider || !subs) return frame; + + try { + if (single_frame != frame_number && single_frame != SUBS_FILE_ALREADY_LOADED) { + // Generally edits and seeks come in groups; if the last thing done + // was seek it is more likely that the user will seek again and + // vice versa. As such, if this is the first frame requested after + // an edit, only export the currently visible lines (because the + // other lines will probably not be viewed before the file changes + // again), and if it's a different frame, export the entire file. + if (single_frame != NEW_SUBS_FILE) { + subs_provider->LoadSubtitles(subs.get()); + single_frame = SUBS_FILE_ALREADY_LOADED; + } + else { + AssFixStylesFilter().ProcessSubs(subs.get(), nullptr); + + single_frame = frame_number; + // Copying a nontrivially sized AssFile is fairly slow, so + // instead muck around with its innards to just temporarily + // remove the non-visible lines without deleting them + std::deque full; + for (auto& line : subs->Line) + full.push_back(&line); + subs->Line.remove_if([=](AssEntry const& e) -> bool { + const AssDialogue *diag = dynamic_cast(&e); + return diag && (diag->Start > time || diag->End <= time); + }); + + try { + subs_provider->LoadSubtitles(subs.get()); + + subs->Line.clear(); + boost::push_back(subs->Line, full | boost::adaptors::indirected); } - else { - AssFixStylesFilter().ProcessSubs(subs.get(), nullptr); - - singleFrame = frameNum; - // Copying a nontrivially sized AssFile is fairly slow, so - // instead muck around with its innards to just temporarily - // remove the non-visible lines without deleting them - std::deque full; - for (auto& line : subs->Line) - full.push_back(&line); - subs->Line.remove_if(invisible_line(time)); - - try { - provider->LoadSubtitles(subs.get()); - - subs->Line.clear(); - boost::push_back(subs->Line, full | boost::adaptors::indirected); - } - catch (...) { - subs->Line.clear(); - boost::push_back(subs->Line, full | boost::adaptors::indirected); - throw; - } + catch (...) { + subs->Line.clear(); + boost::push_back(subs->Line, full | boost::adaptors::indirected); + throw; } } } - catch (wxString const& err) { throw SubtitlesProviderErrorEvent(err); } - - provider->DrawSubtitles(*frame, time); } + catch (std::string const& err) { throw SubtitlesProviderErrorEvent(err); } + + subs_provider->DrawSubtitles(*frame, time / 1000.); + return frame; } -void *ThreadedFrameSource::Entry() { - while (!TestDestroy()) { - double time; - int frameNum; - std::unique_ptr newSubs; - std::deque>> updates; - { - wxMutexLocker jobLocker(jobMutex); - - if (!run) - return EXIT_SUCCESS; - - if (nextTime == -1.) { - jobReady.Wait(); - continue; - } - - time = nextTime; - frameNum = nextFrame; - nextTime = -1.; - newSubs = move(nextSubs); - updates = move(changedSubs); - } - - if (newSubs || updates.size()) { - wxMutexLocker fileLocker(fileMutex); - - if (newSubs) - subs = move(newSubs); - - if (updates.size()) { - size_t i = 0; - auto it = subs->Line.begin(); - // Replace each changed line in subs with the updated version - for (auto& update : updates) { - advance(it, update.first - i); - i = update.first; - subs->Line.insert(it, *update.second.release()); - delete &*it--; - } - } - - singleFrame = NEW_SUBS_FILE; - } - - try { - FrameReadyEvent *evt = new FrameReadyEvent(ProcFrame(frameNum, time), time); - evt->SetEventType(EVT_FRAME_READY); - parent->QueueEvent(evt); - } - catch (wxEvent const& err) { - // Pass error back to parent thread - parent->QueueEvent(err.Clone()); - } - } - - return EXIT_SUCCESS; -} - static SubtitlesProvider *get_subs_provider(wxEvtHandler *parent) { try { return SubtitlesProviderFactory::GetProvider(); } - catch (wxString const& err) { + catch (std::string const& err) { parent->AddPendingEvent(SubtitlesProviderErrorEvent(err)); return 0; } } -ThreadedFrameSource::ThreadedFrameSource(wxString videoFileName, wxEvtHandler *parent) -: wxThread(wxTHREAD_JOINABLE) -, provider(get_subs_provider(parent)) -, videoProvider(VideoProviderFactory::GetProvider(videoFileName)) +ThreadedFrameSource::ThreadedFrameSource(agi::fs::path const& video_filename, wxEvtHandler *parent) +: worker(agi::dispatch::Create()) +, subs_provider(get_subs_provider(parent)) +, video_provider(VideoProviderFactory::GetProvider(video_filename)) , parent(parent) -, nextFrame(-1) -, nextTime(-1.) -, singleFrame(-1) -, jobReady(jobMutex) -, run(true) +, frame_number(-1) +, time(-1.) +, single_frame(-1) +, version(0) { - Create(); - Run(); } ThreadedFrameSource::~ThreadedFrameSource() { - { - wxMutexLocker locker(jobMutex); - run = false; - jobReady.Signal(); - } - Wait(); } -void ThreadedFrameSource::LoadSubtitles(const AssFile *subs) throw() { - AssFile *copy = new AssFile(*subs); - wxMutexLocker locker(jobMutex); - // Set nextSubs and let the worker thread move it to subs so that we don't - // have to lock fileMutex on the GUI thread, as that can be locked for - // extended periods of time with slow-rendering subtitles - nextSubs.reset(copy); - changedSubs.clear(); +void ThreadedFrameSource::LoadSubtitles(const AssFile *new_subs) throw() { + uint_fast32_t req_version = ++version; + + AssFile *copy = new AssFile(*new_subs); + worker->Async([=]{ + subs.reset(copy); + single_frame = NEW_SUBS_FILE; + ProcAsync(req_version); + }); } -void ThreadedFrameSource::UpdateSubtitles(const AssFile *subs, std::set changes) throw() { - std::deque>> changed; +void ThreadedFrameSource::UpdateSubtitles(const AssFile *new_subs, std::set const& changes) throw() { + uint_fast32_t req_version = ++version; + + // Copy just the lines which were changed, then replace the lines at the + // same indices in the worker's copy of the file with the new entries + std::deque> changed; size_t i = 0; - for (auto const& e : subs->Line) { + for (auto const& e : new_subs->Line) { if (changes.count(&e)) - changed.emplace_back(i, std::unique_ptr(e.Clone())); + changed.emplace_back(i, e.Clone()); ++i; } - wxMutexLocker locker(jobMutex); - changedSubs = std::move(changed); + worker->Async([=]{ + size_t i = 0; + auto it = subs->Line.begin(); + for (auto& update : changed) { + advance(it, update.first - i); + i = update.first; + subs->Line.insert(it, *update.second); + delete &*it--; + } + + single_frame = NEW_SUBS_FILE; + ProcAsync(req_version); + }); } -void ThreadedFrameSource::RequestFrame(int frame, double time) throw() { - wxMutexLocker locker(jobMutex); - nextTime = time; - nextFrame = frame; - jobReady.Signal(); +void ThreadedFrameSource::RequestFrame(int new_frame, double new_time) throw() { + uint_fast32_t req_version = ++version; + + worker->Async([=]{ + time = new_time; + frame_number = new_frame; + ProcAsync(req_version); + }); +} + +void ThreadedFrameSource::ProcAsync(uint_fast32_t req_version) { + // Only actually produce the frame if there's no queued changes waiting + if (req_version < version || frame_number < 0) return; + + try { + FrameReadyEvent *evt = new FrameReadyEvent(ProcFrame(frame_number, time), time); + evt->SetEventType(EVT_FRAME_READY); + parent->QueueEvent(evt); + } + catch (wxEvent const& err) { + // Pass error back to parent thread + parent->QueueEvent(err.Clone()); + } } std::shared_ptr ThreadedFrameSource::GetFrame(int frame, double time, bool raw) { - return ProcFrame(frame, time, raw); + std::shared_ptr ret; + std::mutex m; + std::condition_variable cv; + std::unique_lock l(m); + worker->Async([&]{ + std::unique_lock l(m); + ret = ProcFrame(frame, time, raw); + cv.notify_all(); + }); + cv.wait(l, [&]{ return !!ret; }); // predicate is to deal with spurious wakeups + return ret; } wxDEFINE_EVENT(EVT_FRAME_READY, FrameReadyEvent); @@ -250,8 +214,8 @@ VideoProviderErrorEvent::VideoProviderErrorEvent(VideoProviderError const& err) { SetEventType(EVT_VIDEO_ERROR); } -SubtitlesProviderErrorEvent::SubtitlesProviderErrorEvent(wxString err) -: agi::Exception(from_wx(err), nullptr) +SubtitlesProviderErrorEvent::SubtitlesProviderErrorEvent(std::string const& err) +: agi::Exception(err, nullptr) { SetEventType(EVT_SUBTITLES_ERROR); } diff --git a/aegisub/src/threaded_frame_source.h b/aegisub/src/threaded_frame_source.h index 62b101793..dc2e26c84 100644 --- a/aegisub/src/threaded_frame_source.h +++ b/aegisub/src/threaded_frame_source.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012, Thomas Goyne +// Copyright (c) 2013, Thomas Goyne // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -19,15 +19,15 @@ /// @ingroup video /// +#include +#include + +#include #include #include #include #include -#include - -#include -#include class AegiVideoFrame; class AssEntry; @@ -35,39 +35,41 @@ class AssFile; class SubtitlesProvider; class VideoProvider; class VideoProviderError; +namespace agi { namespace dispatch { class Queue; } } /// @class ThreadedFrameSource /// @brief An asynchronous video decoding and subtitle rendering wrapper -class ThreadedFrameSource : public wxThread { +class ThreadedFrameSource { + /// Asynchronous work queue + std::unique_ptr worker; + /// Subtitles provider - agi::scoped_ptr provider; + std::unique_ptr subs_provider; /// Video provider - agi::scoped_ptr videoProvider; + std::unique_ptr video_provider; /// Event handler to send FrameReady events to wxEvtHandler *parent; - int nextFrame; ///< Next queued frame, or -1 for none - double nextTime; ///< Next queued time - std::unique_ptr nextSubs; ///< Next queued AssFile - std::deque>> changedSubs; + int frame_number; ///< Last frame number requested + double time; ///< Time of the frame to pass to the subtitle renderer - /// Subtitles to be loaded the next time a frame is requested + /// Copy of the subtitles file to avoid having to touch the project context std::unique_ptr subs; - /// If subs is set and this is not -1, frame which subs was limited to when - /// it was last sent to the subtitle provider - int singleFrame; - wxMutex fileMutex; ///< Mutex for subs and singleFrame - wxMutex jobMutex; ///< Mutex for nextFrame, nextTime and nextSubs - wxMutex providerMutex; ///< Mutex for video provider - wxMutex evtMutex; ///< Mutex for FrameReadyEvents associated with this + /// If >= 0, the subtitles provider current has just the lines visible on + /// that frame loaded. If -1, the entire file is loaded. If -2, the + /// currently loaded file is out of date. + int single_frame; - wxCondition jobReady; ///< Signal for indicating that a frame has be requested + std::shared_ptr ProcFrame(int frame, double time, bool raw = false); - bool run; ///< Should the thread continue to run + /// Produce a frame if req_version is still the current version + void ProcAsync(uint_fast32_t req_version); + + /// Monotonic counter used to drop frames when changes arrive faster than + /// they can be rendered + std::atomic version; - void *Entry(); - std::shared_ptr ProcFrame(int frameNum, double time, bool raw = false); public: /// @brief Load the passed subtitle file /// @param subs File to load @@ -82,7 +84,7 @@ public: /// /// This function only supports changes to existing lines, and not /// insertions or deletions. - void UpdateSubtitles(const AssFile *subs, std::set changes) throw(); + void UpdateSubtitles(const AssFile *subs, std::set const& changes) throw(); /// @brief Queue a request for a frame /// @brief frame Frame number @@ -99,45 +101,40 @@ public: std::shared_ptr GetFrame(int frame, double time, bool raw = false); /// Get a reference to the video provider this is using - VideoProvider *GetVideoProvider() const { return videoProvider.get(); } + VideoProvider *GetVideoProvider() const { return video_provider.get(); } /// @brief Constructor /// @param videoFileName File to open /// @param parent Event handler to send FrameReady events to - ThreadedFrameSource(wxString videoFileName, wxEvtHandler *parent); + ThreadedFrameSource(agi::fs::path const& filename, wxEvtHandler *parent); ~ThreadedFrameSource(); }; -/// @class FrameReadyEvent -/// @brief Event which signals that a requested frame is ready -class FrameReadyEvent : public wxEvent { -public: +/// Event which signals that a requested frame is ready +struct FrameReadyEvent : public wxEvent { /// Frame which is ready std::shared_ptr frame; /// Time which was used for subtitle rendering double time; wxEvent *Clone() const { return new FrameReadyEvent(*this); }; FrameReadyEvent(std::shared_ptr frame, double time) - : frame(frame), time(time) { - } + : frame(frame), time(time) { } }; // These exceptions are wxEvents so that they can be passed directly back to // the parent thread as events -class VideoProviderErrorEvent : public wxEvent, public agi::Exception { -public: +struct VideoProviderErrorEvent : public wxEvent, public agi::Exception { const char * GetName() const { return "video/error"; } wxEvent *Clone() const { return new VideoProviderErrorEvent(*this); }; agi::Exception *Copy() const { return new VideoProviderErrorEvent(*this); }; VideoProviderErrorEvent(VideoProviderError const& err); }; -class SubtitlesProviderErrorEvent : public wxEvent, public agi::Exception { -public: +struct SubtitlesProviderErrorEvent : public wxEvent, public agi::Exception { const char * GetName() const { return "subtitles/error"; } wxEvent *Clone() const { return new SubtitlesProviderErrorEvent(*this); }; agi::Exception *Copy() const { return new SubtitlesProviderErrorEvent(*this); }; - SubtitlesProviderErrorEvent(wxString msg); + SubtitlesProviderErrorEvent(std::string const& msg); }; wxDECLARE_EVENT(EVT_FRAME_READY, FrameReadyEvent); diff --git a/aegisub/src/timeedit_ctrl.cpp b/aegisub/src/timeedit_ctrl.cpp index 67c53e016..1945b5e19 100644 --- a/aegisub/src/timeedit_ctrl.cpp +++ b/aegisub/src/timeedit_ctrl.cpp @@ -38,11 +38,6 @@ #include -#include -#include -#include -#include - #include "ass_time.h" #include "compat.h" #include "include/aegisub/context.h" @@ -50,6 +45,11 @@ #include "utils.h" #include "video_context.h" +#include +#include +#include +#include + #define TimeEditWindowStyle enum { @@ -57,8 +57,8 @@ enum { Time_Edit_Paste }; -TimeEdit::TimeEdit(wxWindow* parent, wxWindowID id, agi::Context *c, const wxString& value, const wxSize& size, bool asEnd) -: wxTextCtrl(parent, id, value, wxDefaultPosition, size, wxTE_CENTRE | wxTE_PROCESS_ENTER) +TimeEdit::TimeEdit(wxWindow* parent, wxWindowID id, agi::Context *c, const std::string& value, const wxSize& size, bool asEnd) +: wxTextCtrl(parent, id, to_wx(value), wxDefaultPosition, size, wxTE_CENTRE | wxTE_PROCESS_ENTER) , c(c) , byFrame(false) , isEnd(asEnd) @@ -84,7 +84,7 @@ TimeEdit::TimeEdit(wxWindow* parent, wxWindowID id, agi::Context *c, const wxStr SetValidator(val); // Other stuff - if (!value) SetValue(time.GetAssFormated()); + if (value.empty()) SetValue(to_wx(time.GetAssFormated())); Bind(wxEVT_COMMAND_MENU_SELECTED, std::bind(&TimeEdit::CopyTime, this), Time_Edit_Copy); Bind(wxEVT_COMMAND_MENU_SELECTED, std::bind(&TimeEdit::PasteTime, this), Time_Edit_Paste); @@ -116,7 +116,6 @@ void TimeEdit::SetByFrame(bool enableByFrame) { UpdateText(); } - void TimeEdit::OnModified(wxCommandEvent &event) { event.Skip(); if (byFrame) { @@ -125,15 +124,14 @@ void TimeEdit::OnModified(wxCommandEvent &event) { time = c->videoController->TimeAtFrame(temp, isEnd ? agi::vfr::END : agi::vfr::START); } else if (insert) - time = GetValue(); + time = from_wx(GetValue()); } - void TimeEdit::UpdateText() { if (byFrame) ChangeValue(wxString::Format("%d", c->videoController->FrameAtTime(time, isEnd ? agi::vfr::END : agi::vfr::START))); else - ChangeValue(time.GetAssFormated()); + ChangeValue(to_wx(time.GetAssFormated())); } void TimeEdit::OnKeyDown(wxKeyEvent &event) { @@ -168,17 +166,18 @@ void TimeEdit::OnKeyDown(wxKeyEvent &event) { event.Skip(false); - long start = GetInsertionPoint(); - wxString text = GetValue(); - // Delete does nothing if (key == WXK_DELETE) return; + + long start = GetInsertionPoint(); // Back just moves cursor back one without deleting if (key == WXK_BACK) { if (start > 0) SetInsertionPoint(start - 1); return; } + + std::string text = from_wx(GetValue()); // Cursor is at the end so do nothing if (start >= (long)text.size()) return; @@ -193,8 +192,9 @@ void TimeEdit::OnKeyDown(wxKeyEvent &event) { } // Overwrite the digit - time = text.Left(start) + (char)key + text.Mid(start + 1); - SetValue(time.GetAssFormated()); + text[start] = (char)key; + time = text; + SetValue(to_wx(time.GetAssFormated())); SetInsertionPoint(start + 1); } @@ -221,7 +221,7 @@ void TimeEdit::OnFocusLost(wxFocusEvent &evt) { } void TimeEdit::CopyTime() { - SetClipboard(GetValue()); + SetClipboard(from_wx(GetValue())); } void TimeEdit::PasteTime() { @@ -230,8 +230,8 @@ void TimeEdit::PasteTime() { return; } - wxString text = GetClipboard(); - if (!text) return; + std::string text(GetClipboard()); + if (text.empty()) return; AssTime tempTime(text); if (tempTime.GetAssFormated() == text) { diff --git a/aegisub/src/timeedit_ctrl.h b/aegisub/src/timeedit_ctrl.h index 1c3693ce5..7e5a19b1a 100644 --- a/aegisub/src/timeedit_ctrl.h +++ b/aegisub/src/timeedit_ctrl.h @@ -90,5 +90,5 @@ public: /// @param value Initial value. Must be a valid time string or empty /// @param size Initial control size /// @param asEnd Treat the time as a line end time (rather than start) for time <-> frame number conversions - TimeEdit(wxWindow* parent, wxWindowID id, agi::Context *c, const wxString& value = wxString(), const wxSize& size = wxDefaultSize, bool asEnd = false); + TimeEdit(wxWindow* parent, wxWindowID id, agi::Context *c, const std::string& value = std::string(), const wxSize& size = wxDefaultSize, bool asEnd = false); }; diff --git a/aegisub/src/utils.cpp b/aegisub/src/utils.cpp index e0a18df10..f9951c608 100644 --- a/aegisub/src/utils.cpp +++ b/aegisub/src/utils.cpp @@ -36,49 +36,29 @@ #include "utils.h" +#include "compat.h" +#include "options.h" + +#include +#include +#include + #ifdef __UNIX__ #include #endif +#include +#include #include #include -#include #include #include #include -#include - #ifdef __APPLE__ #include #endif -#include "compat.h" -#include "options.h" - -wxDEFINE_EVENT(EVT_CALL_THUNK, wxThreadEvent); - -wxString MakeRelativePath(wxString _path, wxString reference) { - if (_path.empty() || _path[0] == '?') return _path; - wxFileName path(_path); - wxFileName refPath(reference); - path.MakeRelativeTo(refPath.GetPath()); - return path.GetFullPath(); -} - -wxString DecodeRelativePath(wxString _path,wxString reference) { - if (_path.empty() || _path[0] == '?') return _path; - wxFileName path(_path); - wxFileName refPath(reference); - if (!path.IsAbsolute()) path.MakeAbsolute(refPath.GetPath()); -#ifdef __UNIX__ - return path.GetFullPath(wxPATH_UNIX); // also works on windows - // No, it doesn't, this ommits drive letter in Windows. ~ amz -#else - return path.GetFullPath(); -#endif -} - wxString AegiFloatToString(double value) { return wxString::Format("%g",value); } @@ -119,8 +99,7 @@ int SmallestPowerOf2(int x) { return x; } -bool IsWhitespace(wchar_t c) -{ +bool IsWhitespace(wchar_t c) { const wchar_t whitespaces[] = { // http://en.wikipedia.org/wiki/Space_(punctuation)#Table_of_spaces 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x0020, 0x0085, 0x00A0, @@ -128,81 +107,11 @@ bool IsWhitespace(wchar_t c) 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x2028, 0x2029, 0x202F, 0x025F, 0x3000, 0xFEFF }; - const size_t num_chars = sizeof(whitespaces) / sizeof(whitespaces[0]); - return std::binary_search(whitespaces, whitespaces + num_chars, c); + return std::binary_search(whitespaces, std::end(whitespaces), c); } -bool StringEmptyOrWhitespace(const wxString &str) -{ - for (size_t i = 0; i < str.size(); ++i) - if (!IsWhitespace(str[i])) - return false; - - return true; -} - -int AegiStringToInt(const wxString &str,int start,int end) { - // Initialize to zero and get length if end set to -1 - int sign = 1; - int value = 0; - if (end == -1) end = str.Length(); - - for (int pos=start;pos '9') break; - - // Shift value to next decimal place and increment the value just read - value = value * 10 + (val - '0'); - } - - return value*sign; -} - -int AegiStringToFix(const wxString &str,size_t decimalPlaces,int start,int end) { - // Parts of the number - int sign = 1; - int major = 0; - int minor = 0; - if (end == -1) end = str.Length(); - bool inMinor = false; - int *dst = &major; - size_t mCount = 0; - - for (int pos=start;pos '9') break; - *dst = (*dst * 10) + (val - '0'); - mCount++; - } - - // Change minor to have the right number of decimal places - while (mCount > decimalPlaces) { - minor /= 10; - mCount--; - } - while (mCount < decimalPlaces) { - minor *= 10; - mCount++; - } - - // Shift major and return - for (size_t i=0;iOpen()) { @@ -252,13 +161,13 @@ wxString GetClipboard() { } cb->Close(); } - return data; + return from_wx(data); } -void SetClipboard(wxString const& new_data) { +void SetClipboard(std::string const& new_data) { wxClipboard *cb = wxClipboard::Get(); if (cb->Open()) { - cb->SetData(new wxTextDataObject(new_data)); + cb->SetData(new wxTextDataObject(to_wx(new_data))); cb->Close(); cb->Flush(); } @@ -273,64 +182,28 @@ void SetClipboard(wxBitmap const& new_data) { } } -namespace { -class cache_cleaner : public wxThread { - wxString directory; - wxString file_type; - int64_t max_size; - size_t max_files; +void CleanCache(agi::fs::path const& directory, std::string const& file_type, uint64_t max_size, uint64_t max_files) { + static std::unique_ptr queue; + if (!queue) + queue = agi::dispatch::Create(); - ExitCode Entry() { - static wxMutex cleaning_mutex; - wxMutexLocker lock(cleaning_mutex); - - if (!lock.IsOk()) { - LOG_D("utils/clean_cache") << "cleaning already in progress, thread exiting"; - return (ExitCode)1; + max_size <<= 20; + if (max_files == 0) + max_files = std::numeric_limits::max(); + queue->Async([=]{ + LOG_D("utils/clean_cache") << "cleaning " << directory/file_type; + uint64_t total_size = 0; + std::multimap cachefiles; + for (auto const& file : agi::fs::DirectoryIterator(directory, file_type)) { + agi::fs::path path = directory/file; + cachefiles.insert(std::make_pair(agi::fs::ModifiedTime(path), path)); + total_size += agi::fs::Size(path); } - wxDir cachedir; - if (!cachedir.Open(directory)) { - LOG_D("utils/clean_cache") << "couldn't open cache directory " << from_wx(directory); - return (wxThread::ExitCode)1; - } - - // sleep for a bit so we (hopefully) don't thrash the disk too much while indexing is in progress - wxThread::This()->Sleep(2000); - - if (!cachedir.HasFiles(file_type)) { - LOG_D("utils/clean_cache") << "no files of the checked type in directory, exiting"; - return (wxThread::ExitCode)0; - } - - // unusually paranoid sanity check - // (someone might have deleted the file(s) after we did HasFiles() above; does wxDir.Open() lock the dir?) - wxString curfn_str; - if (!cachedir.GetFirst(&curfn_str, file_type, wxDIR_FILES)) { - LOG_D("utils/clean_cache") << "undefined error"; - return (wxThread::ExitCode)1; - } - - int64_t total_size = 0; - std::multimap cachefiles; - do { - wxFileName curfn(directory, curfn_str); - wxDateTime curatime; - curfn.GetTimes(&curatime, nullptr, nullptr); - cachefiles.insert(std::make_pair(curatime.GetTicks(), curfn)); - total_size += curfn.GetSize().GetValue(); - - wxThread::This()->Sleep(250); - } while (cachedir.GetNext(&curfn_str)); - if (cachefiles.size() <= max_files && total_size <= max_size) { - LOG_D("utils/clean_cache") - << "cache does not need cleaning (maxsize=" << max_size - << ", cursize=" << total_size - << "; maxfiles=" << max_files - << ", numfiles=" << cachefiles.size() - << "), exiting"; - return (wxThread::ExitCode)0; + LOG_D("utils/clean_cache") << boost::format("cache does not need cleaning (maxsize=%d, cursize=%d, maxfiles=%d, numfiles=%d), exiting") + % max_size % total_size % max_files % cachefiles.size(); + return; } int deleted = 0; @@ -339,59 +212,25 @@ class cache_cleaner : public wxThread { if ((total_size <= max_size && cachefiles.size() - deleted <= max_files) || cachefiles.size() - deleted < 2) break; - int64_t fsize = i.second.GetSize().GetValue(); -#ifdef __WXMSW__ - int res = wxRemove(i.second.GetFullPath()); -#else - int res = unlink(i.second.GetFullPath().fn_str()); -#endif - if (res) { - LOG_D("utils/clean_cache") << "failed to remove file " << from_wx(i.second.GetFullPath()); + uint64_t size = agi::fs::Size(i.second); + try { + agi::fs::Remove(i.second); + LOG_D("utils/clean_cache") << "deleted " << i.second; + } + catch (agi::Exception const& e) { + LOG_D("utils/clean_cache") << "failed to delete file " << i.second << ": " << e.GetChainedMessage(); continue; } - total_size -= fsize; + total_size -= size; ++deleted; - - wxThread::This()->Sleep(250); } LOG_D("utils/clean_cache") << "deleted " << deleted << " files, exiting"; - return (wxThread::ExitCode)0; - } - -public: - cache_cleaner(wxString const& directory, wxString const& file_type, int64_t max_size, int64_t max_files) - : wxThread(wxTHREAD_DETACHED) - , directory(directory) - , file_type(file_type) - , max_size(max_size << 20) - { - if (max_files < 1) - this->max_files = (size_t)-1; - else - this->max_files = (size_t)max_files; - } -}; + }); } -void CleanCache(wxString const& directory, wxString const& file_type, int64_t max_size, int64_t max_files) { - LOG_D("utils/clean_cache") << "attempting to start cleaner thread"; - wxThread *CleaningThread = new cache_cleaner(directory, file_type, max_size, max_files); - - if (CleaningThread->Create() != wxTHREAD_NO_ERROR) { - LOG_D("utils/clean_cache") << "thread creation failed"; - delete CleaningThread; - } - else if (CleaningThread->Run() != wxTHREAD_NO_ERROR) { - LOG_D("utils/clean_cache") << "failed to start thread"; - delete CleaningThread; - } - - LOG_D("utils/clean_cache") << "thread started successfully"; -} - -size_t MaxLineLength(wxString const& text) { +size_t MaxLineLength(std::string const& text) { size_t max_line_length = 0; size_t current_line_length = 0; bool last_was_slash = false; @@ -441,3 +280,4 @@ size_t MaxLineLength(wxString const& text) { void AddFullScreenButton(wxWindow *) { } void SetFloatOnParent(wxWindow *) { } #endif + diff --git a/aegisub/src/utils.h b/aegisub/src/utils.h index 1ed48b3b3..aa31bdc7a 100644 --- a/aegisub/src/utils.h +++ b/aegisub/src/utils.h @@ -32,12 +32,12 @@ /// @ingroup utility /// - #pragma once -#include +#include #include +#include #include #include #include @@ -50,10 +50,6 @@ class wxMouseEvent; class wxWindow; -/// @brief Make a path relative to reference -wxString MakeRelativePath(wxString path,wxString reference); -/// @brief Extract original path from relative -wxString DecodeRelativePath(wxString path,wxString reference); wxString AegiFloatToString(double value); wxString AegiIntegerToString(int value); wxString PrettySize(int bytes); @@ -69,14 +65,8 @@ bool IsWhitespace(wchar_t c); /// Check if every character in str is whitespace bool StringEmptyOrWhitespace(const wxString &str); -/// @brief String to integer -/// -/// wxString::ToLong() is slow and not as flexible -int AegiStringToInt(const wxString &str,int start=0,int end=-1); -int AegiStringToFix(const wxString &str,size_t decimalPlaces,int start=0,int end=-1); - /// Get the length in characters of the longest line in the given text -size_t MaxLineLength(wxString const& text); +size_t MaxLineLength(std::string const& text); /// @brief Launch a new copy of Aegisub. /// @@ -102,7 +92,7 @@ bool ForwardMouseWheelEvent(wxWindow *source, wxMouseEvent &evt); /// @param file_type Wildcard pattern for files to clean up /// @param max_size Maximum size of directory in MB /// @param max_files Maximum number of files -void CleanCache(wxString const& directory, wxString const& file_type, int64_t max_size, int64_t max_files = -1); +void CleanCache(agi::fs::path const& directory, std::string const& file_type, uint64_t max_size, uint64_t max_files = -1); /// @brief Templated abs() function template T tabs(T x) { return x < 0 ? -x : x; } @@ -112,9 +102,9 @@ template T tabs(T x) { return x < 0 ? -x : x; } template inline T mid(T a, T b, T c) { return std::max(a, std::min(b, c)); } /// Get the text contents of the clipboard, or empty string on failure -wxString GetClipboard(); +std::string GetClipboard(); /// Try to set the clipboard to the given string -void SetClipboard(wxString const& new_value); +void SetClipboard(std::string const& new_value); void SetClipboard(wxBitmap const& new_value); #ifndef FORCEINLINE @@ -144,36 +134,6 @@ void delete_clear(T& container) { } } -/// Helper class for background_delete_clear -template -class BackgroundDeleter : public wxThread { - Container cont; - wxThread::ExitCode Entry() { - cont.clear_and_dispose(delete_ptr()); - return (wxThread::ExitCode)0; - } -public: - BackgroundDeleter(Container &source) - : wxThread(wxTHREAD_DETACHED) - { - using std::swap; - swap(cont, source); - - Create(); - SetPriority(WXTHREAD_MIN_PRIORITY); - Run(); - } -}; - -/// Clear a container of pointers and delete the pointed to members on a -/// background thread -template -void background_delete_clear(T& container) { - if (!container.empty()) - new BackgroundDeleter(container); -} - - template struct cast { template @@ -187,18 +147,5 @@ struct cast { } }; -wxDECLARE_EVENT(EVT_CALL_THUNK, wxThreadEvent); - -template -void InvokeOnMainThreadAsync(Function const& f) { - wxThreadEvent *evt = new wxThreadEvent(EVT_CALL_THUNK); - evt->SetPayload>(f); - wxTheApp->QueueEvent(evt); -} - -template -void InvokeOnMainThread(Function const& f) { - wxSemaphore sema(0, 1); - InvokeOnMainThreadAsync([&] { f(); sema.Post(); }); - sema.Wait(); -} +agi::fs::path OpenFileSelector(wxString const& message, std::string const& option_name, std::string const& default_filename, std::string const& default_extension, wxString const& wildcard, wxWindow *parent); +agi::fs::path SaveFileSelector(wxString const& message, std::string const& option_name, std::string const& default_filename, std::string const& default_extension, wxString const& wildcard, wxWindow *parent); diff --git a/aegisub/src/validators.cpp b/aegisub/src/validators.cpp index 080d2d9bf..9cbce4b73 100644 --- a/aegisub/src/validators.cpp +++ b/aegisub/src/validators.cpp @@ -34,11 +34,16 @@ #include "config.h" -#include - -#include "utils.h" #include "validators.h" +#include "compat.h" +#include "utils.h" + +#include + +#include +#include + NumValidator::NumValidator(wxString val, bool isfloat, bool issigned) : fValue(0) , iValue(0) @@ -202,3 +207,25 @@ bool NumValidator::TransferFromWindow() { return true; } + +bool StringBinder::TransferFromWindow() { + wxWindow *window = GetWindow(); + if (wxTextCtrl *ctrl = dynamic_cast(window)) + *value = from_wx(ctrl->GetValue()); + else if (wxComboBox *ctrl = dynamic_cast(window)) + *value = from_wx(ctrl->GetValue()); + else + throw agi::InternalError("Unsupported control type", 0); + return true; +} + +bool StringBinder::TransferToWindow() { + wxWindow *window = GetWindow(); + if (wxTextCtrl *ctrl = dynamic_cast(window)) + ctrl->SetValue(to_wx(*value)); + else if (wxComboBox *ctrl = dynamic_cast(window)) + ctrl->SetValue(to_wx(*value)); + else + throw agi::InternalError("Unsupported control type", 0); + return true; +} diff --git a/aegisub/src/validators.h b/aegisub/src/validators.h index fc07ec24b..51bdf4bb7 100644 --- a/aegisub/src/validators.h +++ b/aegisub/src/validators.h @@ -34,6 +34,8 @@ #include +#include + #include #include @@ -123,3 +125,15 @@ template EnumBinder MakeEnumBinder(T *value) { return EnumBinder(value); } + +class StringBinder : public wxValidator { + std::string *value; + + wxObject* Clone() const { return new StringBinder(value); } + bool Validate(wxWindow*) { return true;} + bool TransferToWindow(); + bool TransferFromWindow(); + +public: + explicit StringBinder(std::string *value) : value(value) { } +}; diff --git a/aegisub/src/vector2d.cpp b/aegisub/src/vector2d.cpp index b39ea6a56..ab6d492a9 100644 --- a/aegisub/src/vector2d.cpp +++ b/aegisub/src/vector2d.cpp @@ -23,6 +23,7 @@ #include "vector2d.h" +#include #include Vector2D::Vector2D() @@ -77,21 +78,21 @@ Vector2D::operator unspecified_bool_type() const { return *this == Vector2D() ? 0 : &Vector2D::x; } -wxString Vector2D::PStr(char sep) const { +std::string Vector2D::PStr(char sep) const { return "(" + Str(sep) + ")"; } -wxString Vector2D::DStr(char sep) const { - return wxString::Format("%d%c%d", (int)x, sep, (int)y); +std::string Vector2D::DStr(char sep) const { + return str(boost::format("%d%c%d") % (int)x % sep % (int)y); } -static wxString float_to_string(float val) { - wxString s = wxString::Format("%.3f", val); +static std::string float_to_string(float val) { + std::string s = str(boost::format("%.3f") % val); size_t pos = s.find_last_not_of("0"); if (pos != s.find(".")) ++pos; - return s.Left(pos); + return s.substr(0, pos); } -wxString Vector2D::Str(char sep) const { +std::string Vector2D::Str(char sep) const { return float_to_string(x) + sep + float_to_string(y); } diff --git a/aegisub/src/vector2d.h b/aegisub/src/vector2d.h index 146192f11..41811c709 100644 --- a/aegisub/src/vector2d.h +++ b/aegisub/src/vector2d.h @@ -22,7 +22,7 @@ #pragma once #include - +#include #include class Vector2D { @@ -71,11 +71,11 @@ public: float Angle() const { return atan2(y, x); } /// Get as string with given separator - wxString Str(char sep = ',') const; + std::string Str(char sep = ',') const; /// Get as string surrounded by parentheses with given separator - wxString PStr(char sep = ',') const; + std::string PStr(char sep = ',') const; /// Get as string with given separator with values rounded to ints - wxString DStr(char sep = ',') const; + std::string DStr(char sep = ',') const; static Vector2D FromAngle(float angle) { return Vector2D(cos(-angle), sin(-angle)); } }; diff --git a/aegisub/src/video_context.cpp b/aegisub/src/video_context.cpp index f2d6e9301..0cd32b060 100644 --- a/aegisub/src/video_context.cpp +++ b/aegisub/src/video_context.cpp @@ -34,20 +34,10 @@ #include "config.h" -#include - -#include -#include -#include -#include -#include - -#include -#include +#include "video_context.h" #include "ass_dialogue.h" #include "ass_file.h" -#include "ass_style.h" #include "ass_time.h" #include "audio_controller.h" #include "compat.h" @@ -56,21 +46,25 @@ #include "mkv_wrap.h" #include "options.h" #include "selection_controller.h" -#include "standard_paths.h" #include "time_range.h" #include "threaded_frame_source.h" #include "utils.h" -#include "video_context.h" #include "video_frame.h" +#include +#include +#include + +#include + VideoContext::VideoContext() : playback(this) -, startMS(0) -, endFrame(0) +, start_ms(0) +, end_frame(0) , frame_n(0) -, arValue(1.) -, arType(0) -, hasSubtitles(false) +, ar_value(1.) +, ar_type(0) +, has_subtitles(false) , playAudioOnStep(OPT_GET("Audio/Plays When Stepping Video")) , no_amend(false) { @@ -99,24 +93,24 @@ VideoContext *VideoContext::Get() { } void VideoContext::Reset() { - StandardPaths::SetPathValue("?video", ""); + config::path->SetToken("?video", ""); // Remove video data Stop(); frame_n = 0; // Clean up video data - videoFile.clear(); + video_filename.clear(); // Remove provider provider.reset(); - videoProvider = 0; + video_provider = 0; - keyFrames.clear(); - keyFramesFilename.clear(); - videoFPS = agi::vfr::Framerate(); - KeyframesOpen(keyFrames); - if (!ovrFPS.IsLoaded()) TimecodesOpen(videoFPS); + keyframes.clear(); + keyframes_filename.clear(); + video_fps = agi::vfr::Framerate(); + KeyframesOpen(keyframes); + if (!ovr_fps.IsLoaded()) TimecodesOpen(video_fps); } void VideoContext::SetContext(agi::Context *context) { @@ -125,7 +119,7 @@ void VideoContext::SetContext(agi::Context *context) { context->ass->AddFileSaveListener(&VideoContext::OnSubtitlesSave, this); } -void VideoContext::SetVideo(const wxString &filename) { +void VideoContext::SetVideo(const agi::fs::path &filename) { Reset(); if (filename.empty()) { VideoOpen(); @@ -135,8 +129,8 @@ void VideoContext::SetVideo(const wxString &filename) { bool commit_subs = false; try { provider.reset(new ThreadedFrameSource(filename, this)); - videoProvider = provider->GetVideoProvider(); - videoFile = filename; + video_provider = provider->GetVideoProvider(); + video_filename = filename; // Check that the script resolution matches the video resolution int sx = context->ass->GetScriptInfoAsInt("PlayResX"); @@ -147,8 +141,8 @@ void VideoContext::SetVideo(const wxString &filename) { // If the script resolution hasn't been set at all just force it to the // video resolution if (sx == 0 && sy == 0) { - context->ass->SetScriptInfo("PlayResX", wxString::Format("%d", vx)); - context->ass->SetScriptInfo("PlayResY", wxString::Format("%d", vy)); + context->ass->SetScriptInfo("PlayResX", std::to_string(vx)); + context->ass->SetScriptInfo("PlayResY", std::to_string(vy)); commit_subs = true; } // If it has been set to something other than a multiple of the video @@ -165,8 +159,8 @@ void VideoContext::SetVideo(const wxString &filename) { break; // Fallthrough to case 2 case 2: // Always change script res - context->ass->SetScriptInfo("PlayResX", wxString::Format("%d", vx)); - context->ass->SetScriptInfo("PlayResY", wxString::Format("%d", vy)); + context->ass->SetScriptInfo("PlayResX", std::to_string(vx)); + context->ass->SetScriptInfo("PlayResY", std::to_string(vy)); commit_subs = true; break; default: // Never change @@ -174,44 +168,44 @@ void VideoContext::SetVideo(const wxString &filename) { } } - keyFrames = videoProvider->GetKeyFrames(); + keyframes = video_provider->GetKeyFrames(); // Set frame rate - videoFPS = videoProvider->GetFPS(); - if (ovrFPS.IsLoaded()) { + video_fps = video_provider->GetFPS(); + if (ovr_fps.IsLoaded()) { int ovr = wxMessageBox(_("You already have timecodes loaded. Would you like to replace them with timecodes from the video file?"), _("Replace timecodes?"), wxYES_NO | wxICON_QUESTION); if (ovr == wxYES) { - ovrFPS = agi::vfr::Framerate(); - ovrTimecodeFile.clear(); + ovr_fps = agi::vfr::Framerate(); + timecodes_filename.clear(); } } // Set aspect ratio - double dar = videoProvider->GetDAR(); + double dar = video_provider->GetDAR(); if (dar > 0) SetAspectRatio(4, dar); // Set filename - config::mru->Add("Video", from_wx(filename)); - StandardPaths::SetPathValue("?video", wxFileName(filename).GetPath()); + config::mru->Add("Video", filename); + config::path->SetToken("?video", filename); // Show warning - wxString warning = videoProvider->GetWarning(); - if (!warning.empty()) wxMessageBox(warning, "Warning", wxICON_WARNING | wxOK); + std::string warning = video_provider->GetWarning(); + if (!warning.empty()) + wxMessageBox(to_wx(warning), "Warning", wxICON_WARNING | wxOK); - hasSubtitles = false; - if (filename.Right(4).Lower() == ".mkv") { - hasSubtitles = MatroskaWrapper::HasSubtitles(filename); - } + has_subtitles = false; + if (agi::fs::HasExtension(filename, "mkv")) + has_subtitles = MatroskaWrapper::HasSubtitles(filename); provider->LoadSubtitles(context->ass); VideoOpen(); - KeyframesOpen(keyFrames); + KeyframesOpen(keyframes); TimecodesOpen(FPS()); } catch (agi::UserCancelException const&) { } - catch (agi::FileNotAccessibleError const& err) { - config::mru->Remove("Video", from_wx(filename)); + catch (agi::fs::FileSystemError const& err) { + config::mru->Remove("Video", filename); wxMessageBox(to_wx(err.GetMessage()), "Error setting video", wxOK | wxICON_ERROR | wxCENTER); } catch (VideoProviderError const& err) { @@ -227,7 +221,7 @@ void VideoContext::SetVideo(const wxString &filename) { void VideoContext::Reload() { if (IsLoaded()) { int frame = frame_n; - SetVideo(videoFile.Clone()); + SetVideo(agi::fs::path(video_filename)); // explicitly copy videoFile since it's cleared in SetVideo JumpToFrame(frame); } } @@ -248,8 +242,8 @@ void VideoContext::OnSubtitlesCommit(int type, std::set const& void VideoContext::OnSubtitlesSave() { no_amend = true; - context->ass->SetScriptInfo("VFR File", MakeRelativePath(GetTimecodesName(), context->ass->filename)); - context->ass->SetScriptInfo("Keyframes File", MakeRelativePath(GetKeyFramesName(), context->ass->filename)); + context->ass->SetScriptInfo("VFR File", config::path->MakeRelative(GetTimecodesName(), "?script").generic_string()); + context->ass->SetScriptInfo("Keyframes File", config::path->MakeRelative(GetKeyFramesName(), "?script").generic_string()); if (!IsLoaded()) { context->ass->SetScriptInfo("Video File", ""); @@ -258,16 +252,16 @@ void VideoContext::OnSubtitlesSave() { return; } - wxString ar; - if (arType == 4) - ar = wxString::Format("c%g", arValue); + std::string ar; + if (ar_type == 4) + ar = "c" + std::to_string(ar_value); else - ar = wxString::Format("%d", arType); + ar = "c" + std::to_string(ar_type); - context->ass->SetScriptInfo("Video File", MakeRelativePath(videoFile, context->ass->filename)); - context->ass->SetScriptInfo("YCbCr Matrix", videoProvider->GetColorSpace()); + context->ass->SetScriptInfo("Video File", config::path->MakeRelative(video_filename, "?script").generic_string()); + context->ass->SetScriptInfo("YCbCr Matrix", video_provider->GetColorSpace()); context->ass->SetScriptInfo("Video Aspect Ratio", ar); - context->ass->SetScriptInfo("Video Position", wxString::Format("%d", frame_n)); + context->ass->SetScriptInfo("Video Position", std::to_string(frame_n)); } void VideoContext::JumpToFrame(int n) { @@ -291,44 +285,33 @@ void VideoContext::JumpToTime(int ms, agi::vfr::Time end) { } void VideoContext::GetFrameAsync(int n) { - provider->RequestFrame(n, TimeAtFrame(n) / 1000.0); + provider->RequestFrame(n, TimeAtFrame(n)); } std::shared_ptr VideoContext::GetFrame(int n, bool raw) { - return provider->GetFrame(n, TimeAtFrame(n) / 1000.0, raw); + return provider->GetFrame(n, TimeAtFrame(n), raw); } -int VideoContext::GetWidth() const { - return videoProvider->GetWidth(); -} -int VideoContext::GetHeight() const { - return videoProvider->GetHeight(); -} - -int VideoContext::GetLength() const { - return videoProvider->GetFrameCount(); -} +int VideoContext::GetWidth() const { return video_provider->GetWidth(); } +int VideoContext::GetHeight() const { return video_provider->GetHeight(); } +int VideoContext::GetLength() const { return video_provider->GetFrameCount(); } void VideoContext::NextFrame() { - if (!videoProvider || IsPlaying() || frame_n == videoProvider->GetFrameCount()) + if (!video_provider || IsPlaying() || frame_n == video_provider->GetFrameCount()) return; JumpToFrame(frame_n + 1); - // Start playing audio - if (playAudioOnStep->GetBool()) { + if (playAudioOnStep->GetBool()) context->audioController->PlayRange(TimeRange(TimeAtFrame(frame_n - 1), TimeAtFrame(frame_n))); - } } void VideoContext::PrevFrame() { - if (!videoProvider || IsPlaying() || frame_n == 0) + if (!video_provider || IsPlaying() || frame_n == 0) return; JumpToFrame(frame_n - 1); - // Start playing audio - if (playAudioOnStep->GetBool()) { + if (playAudioOnStep->GetBool()) context->audioController->PlayRange(TimeRange(TimeAtFrame(frame_n), TimeAtFrame(frame_n + 1))); - } } void VideoContext::Play() { @@ -339,15 +322,12 @@ void VideoContext::Play() { if (!IsLoaded()) return; - // Set variables - startMS = TimeAtFrame(frame_n); - endFrame = GetLength() - 1; + start_ms = TimeAtFrame(frame_n); + end_frame = GetLength() - 1; - // Start playing audio - context->audioController->PlayToEnd(startMS); + context->audioController->PlayToEnd(start_ms); - // Start timer - playTime.Start(); + playback_start_time = std::chrono::steady_clock::now(); playback.Start(10); } @@ -357,19 +337,16 @@ void VideoContext::PlayLine() { AssDialogue *curline = context->selectionController->GetActiveLine(); if (!curline) return; - // Start playing audio context->audioController->PlayRange(TimeRange(curline->Start, curline->End)); // Round-trip conversion to convert start to exact - int startFrame = FrameAtTime(context->selectionController->GetActiveLine()->Start,agi::vfr::START); - startMS = TimeAtFrame(startFrame); - endFrame = FrameAtTime(context->selectionController->GetActiveLine()->End,agi::vfr::END) + 1; + int startFrame = FrameAtTime(context->selectionController->GetActiveLine()->Start, agi::vfr::START); + start_ms = TimeAtFrame(startFrame); + end_frame = FrameAtTime(context->selectionController->GetActiveLine()->End, agi::vfr::END) + 1; - // Jump to start JumpToFrame(startFrame); - // Start timer - playTime.Start(); + playback_start_time = std::chrono::steady_clock::now(); playback.Start(10); } @@ -381,21 +358,17 @@ void VideoContext::Stop() { } void VideoContext::OnPlayTimer(wxTimerEvent &) { - int nextFrame = FrameAtTime(startMS + playTime.Time()); + using namespace std::chrono; + int next_frame = FrameAtTime(start_ms + duration_cast(steady_clock::now() - playback_start_time).count()); + if (next_frame == frame_n) return; - // Same frame - if (nextFrame == frame_n) return; - - // End - if (nextFrame >= endFrame) { + if (next_frame >= end_frame) Stop(); - return; + else { + frame_n = next_frame; + GetFrameAsync(frame_n); + Seek(frame_n); } - - // Jump to next frame - frame_n = nextFrame; - GetFrameAsync(frame_n); - Seek(frame_n); } double VideoContext::GetARFromType(int type) const { @@ -409,87 +382,83 @@ double VideoContext::GetARFromType(int type) const { void VideoContext::SetAspectRatio(int type, double value) { if (type != 4) value = GetARFromType(type); - arType = type; - arValue = mid(.5, value, 5.); - ARChange(arType, arValue); + ar_type = type; + ar_value = mid(.5, value, 5.); + ARChange(ar_type, ar_value); } -void VideoContext::LoadKeyframes(wxString filename) { - if (filename == keyFramesFilename || filename.empty()) return; +void VideoContext::LoadKeyframes(agi::fs::path const& filename) { + if (filename == keyframes_filename || filename.empty()) return; try { - keyFrames = agi::keyframe::Load(from_wx(filename)); - keyFramesFilename = filename; - KeyframesOpen(keyFrames); - config::mru->Add("Keyframes", from_wx(filename)); + keyframes = agi::keyframe::Load(filename); + keyframes_filename = filename; + KeyframesOpen(keyframes); + config::mru->Add("Keyframes", filename); } catch (agi::keyframe::Error const& err) { wxMessageBox(to_wx(err.GetMessage()), "Error opening keyframes file", wxOK | wxICON_ERROR | wxCENTER, context->parent); - config::mru->Remove("Keyframes", from_wx(filename)); + config::mru->Remove("Keyframes", filename); } - catch (agi::FileSystemError const&) { - wxLogError("Could not open file " + filename); - config::mru->Remove("Keyframes", from_wx(filename)); + catch (agi::fs::FileSystemError const& err) { + wxMessageBox(to_wx(err.GetMessage()), "Error opening keyframes file", wxOK | wxICON_ERROR | wxCENTER, context->parent); + config::mru->Remove("Keyframes", filename); } } -void VideoContext::SaveKeyframes(wxString filename) { - agi::keyframe::Save(from_wx(filename), GetKeyFrames()); - config::mru->Add("Keyframes", from_wx(filename)); +void VideoContext::SaveKeyframes(agi::fs::path const& filename) { + agi::keyframe::Save(filename, GetKeyFrames()); + config::mru->Add("Keyframes", filename); } void VideoContext::CloseKeyframes() { - keyFramesFilename.clear(); - if (videoProvider) - keyFrames = videoProvider->GetKeyFrames(); + keyframes_filename.clear(); + if (video_provider) + keyframes = video_provider->GetKeyFrames(); else - keyFrames.clear(); - KeyframesOpen(keyFrames); + keyframes.clear(); + KeyframesOpen(keyframes); } -void VideoContext::LoadTimecodes(wxString filename) { - if (filename == ovrTimecodeFile || filename.empty()) return; +void VideoContext::LoadTimecodes(agi::fs::path const& filename) { + if (filename == timecodes_filename || filename.empty()) return; try { - ovrFPS = agi::vfr::Framerate(from_wx(filename)); - ovrTimecodeFile = filename; - config::mru->Add("Timecodes", from_wx(filename)); + ovr_fps = agi::vfr::Framerate(filename); + timecodes_filename = filename; + config::mru->Add("Timecodes", filename); OnSubtitlesCommit(0, std::set()); - TimecodesOpen(ovrFPS); + TimecodesOpen(ovr_fps); } - catch (const agi::FileSystemError&) { - wxLogError("Could not open file " + filename); - config::mru->Remove("Timecodes", from_wx(filename)); + catch (agi::fs::FileSystemError const& err) { + wxMessageBox(to_wx(err.GetMessage()), "Error opening timecodes file", wxOK | wxICON_ERROR | wxCENTER, context->parent); + config::mru->Remove("Timecodes", filename); } catch (const agi::vfr::Error& e) { - wxLogError("Timecode file parse error: %s", e.GetMessage()); + wxLogError("Timecode file parse error: %s", to_wx(e.GetMessage())); + config::mru->Remove("Timecodes", filename); } } -void VideoContext::SaveTimecodes(wxString filename) { +void VideoContext::SaveTimecodes(agi::fs::path const& filename) { try { - FPS().Save(from_wx(filename), IsLoaded() ? GetLength() : -1); - config::mru->Add("Timecodes", from_wx(filename)); + FPS().Save(filename, IsLoaded() ? GetLength() : -1); + config::mru->Add("Timecodes", filename); } - catch(const agi::FileSystemError&) { - wxLogError("Could not write to " + filename); + catch (agi::fs::FileSystemError const& err) { + wxMessageBox(to_wx(err.GetMessage()), "Error saving timecodes", wxOK | wxICON_ERROR | wxCENTER, context->parent); } } void VideoContext::CloseTimecodes() { - ovrFPS = agi::vfr::Framerate(); - ovrTimecodeFile.clear(); + ovr_fps = agi::vfr::Framerate(); + timecodes_filename.clear(); OnSubtitlesCommit(0, std::set()); - TimecodesOpen(videoFPS); + TimecodesOpen(video_fps); } int VideoContext::TimeAtFrame(int frame, agi::vfr::Time type) const { - if (ovrFPS.IsLoaded()) { - return ovrFPS.TimeAtFrame(frame, type); - } - return videoFPS.TimeAtFrame(frame, type); + return (ovr_fps.IsLoaded() ? ovr_fps : video_fps).TimeAtFrame(frame, type); } + int VideoContext::FrameAtTime(int time, agi::vfr::Time type) const { - if (ovrFPS.IsLoaded()) { - return ovrFPS.FrameAtTime(time, type); - } - return videoFPS.FrameAtTime(time, type); + return (ovr_fps.IsLoaded() ? ovr_fps : video_fps).FrameAtTime(time, type); } void VideoContext::OnVideoError(VideoProviderErrorEvent const& err) { @@ -503,10 +472,3 @@ void VideoContext::OnSubtitlesError(SubtitlesProviderErrorEvent const& err) { "Failed rendering subtitles. Error message reported: %s", to_wx(err.GetMessage())); } - -void VideoContext::OnExit() { - // On unix wxThreadModule will shut down any still-running threads (and - // display a warning that it's doing so) before the destructor for - // VideoContext runs, so manually kill the thread - Get()->provider.reset(); -} diff --git a/aegisub/src/video_context.h b/aegisub/src/video_context.h index d69ac9749..eb6a5b91b 100644 --- a/aegisub/src/video_context.h +++ b/aegisub/src/video_context.h @@ -32,24 +32,25 @@ /// @ingroup video /// +#include +#include +#include + +#include +#include #include #include #include #include #include -#include - -#include -#include -#include class AegiVideoFrame; class AssEntry; -class SubtitlesProviderErrorEvent; +struct SubtitlesProviderErrorEvent; class ThreadedFrameSource; class VideoProvider; -class VideoProviderErrorEvent; +struct VideoProviderErrorEvent; namespace agi { struct Context; @@ -78,33 +79,33 @@ class VideoContext : public wxEvtHandler { /// The video provider owned by the threaded frame source, or nullptr if no /// video is open - VideoProvider *videoProvider; + VideoProvider *video_provider; /// Asynchronous provider of video frames - agi::scoped_ptr provider; + std::unique_ptr provider; /// Filename of currently open video - wxString videoFile; + agi::fs::path video_filename; /// List of frame numbers which are keyframes - std::vector keyFrames; + std::vector keyframes; /// File name of the currently open keyframes or empty if keyframes are not overridden - wxString keyFramesFilename; + agi::fs::path keyframes_filename; /// Playback timer used to periodically check if we should go to the next /// frame while playing video wxTimer playback; - /// Time since playback was last started - wxStopWatch playTime; + /// Time when playback was last started + std::chrono::steady_clock::time_point playback_start_time; /// The start time of the first frame of the current playback; undefined if /// video is not currently playing - int startMS; + int start_ms; /// The last frame to play if video is currently playing - int endFrame; + int end_frame; /// The frame number which was last requested from the video provider, /// which may not be the same thing as the currently displayed frame @@ -112,20 +113,20 @@ class VideoContext : public wxEvtHandler { /// The picture aspect ratio of the video if the aspect ratio has been /// overridden by the user - double arValue; + double ar_value; /// @brief The current AR type /// /// 0 is square pixels; 1-3 are predefined ARs; 4 is custom, where the real /// AR is in arValue - int arType; + int ar_type; /// Does the currently loaded video file have subtitles muxed into it? - bool hasSubtitles; + bool has_subtitles; /// Filename of the currently loaded timecodes file, or empty if timecodes /// have not been overridden - wxString ovrTimecodeFile; + agi::fs::path timecodes_filename; /// Cached option for audio playing when frame stepping const agi::OptionValue* playAudioOnStep; @@ -139,9 +140,9 @@ class VideoContext : public wxEvtHandler { void OnPlayTimer(wxTimerEvent &event); /// The timecodes from the video file - agi::vfr::Framerate videoFPS; + agi::vfr::Framerate video_fps; /// External timecode which have been loaded, if any - agi::vfr::Framerate ovrFPS; + agi::vfr::Framerate ovr_fps; void OnVideoError(VideoProviderErrorEvent const& err); void OnSubtitlesError(SubtitlesProviderErrorEvent const& err); @@ -164,7 +165,7 @@ public: void SetContext(agi::Context *context); /// @brief Get the video provider used for the currently open video - VideoProvider *GetProvider() const { return videoProvider; } + VideoProvider *GetProvider() const { return video_provider; } /// Synchronously get a video frame /// @param n Frame number to get @@ -177,16 +178,16 @@ public: void GetFrameAsync(int n); /// Is there a video loaded? - bool IsLoaded() const { return !!videoProvider; } + bool IsLoaded() const { return !!video_provider; } /// Get the file name of the currently open video, if any - wxString GetVideoName() const { return videoFile; } + agi::fs::path GetVideoName() const { return video_filename; } /// Is the video currently playing? bool IsPlaying() const { return playback.IsRunning(); } /// Does the video file loaded have muxed subtitles that we can load? - bool HasSubtitles() const { return hasSubtitles; } + bool HasSubtitles() const { return has_subtitles; } /// Get the width of the currently open video int GetWidth() const; @@ -209,14 +210,14 @@ public: void SetAspectRatio(int type, double value=1.0); /// Get the current AR type - int GetAspectRatioType() const { return arType; } + int GetAspectRatioType() const { return ar_type; } /// Get the current aspect ratio of the video - double GetAspectRatioValue() const { return arValue; } + double GetAspectRatioValue() const { return ar_value; } /// @brief Open a new video /// @param filename Video to open, or empty to close the current video - void SetVideo(const wxString &filename); + void SetVideo(const agi::fs::path &filename); /// @brief Close and reopen the current video void Reload(); @@ -245,27 +246,26 @@ public: DEFINE_SIGNAL_ADDERS(TimecodesOpen, AddTimecodesListener) DEFINE_SIGNAL_ADDERS(ARChange, AddARChangeListener) - const std::vector& GetKeyFrames() const { return keyFrames; }; - wxString GetKeyFramesName() const { return keyFramesFilename; } - void LoadKeyframes(wxString filename); - void SaveKeyframes(wxString filename); + const std::vector& GetKeyFrames() const { return keyframes; }; + agi::fs::path GetKeyFramesName() const { return keyframes_filename; } + void LoadKeyframes(agi::fs::path const& filename); + void SaveKeyframes(agi::fs::path const& filename); void CloseKeyframes(); - bool OverKeyFramesLoaded() const { return !keyFramesFilename.empty(); } - bool KeyFramesLoaded() const { return !keyFrames.empty(); } + bool OverKeyFramesLoaded() const { return !keyframes_filename.empty(); } + bool KeyFramesLoaded() const { return !keyframes.empty(); } - wxString GetTimecodesName() const { return ovrTimecodeFile; } - void LoadTimecodes(wxString filename); - void SaveTimecodes(wxString filename); + agi::fs::path GetTimecodesName() const { return timecodes_filename; } + void LoadTimecodes(agi::fs::path const& filename); + void SaveTimecodes(agi::fs::path const& filename); void CloseTimecodes(); - bool OverTimecodesLoaded() const { return ovrFPS.IsLoaded(); } - bool TimecodesLoaded() const { return videoFPS.IsLoaded() || ovrFPS.IsLoaded(); }; + bool OverTimecodesLoaded() const { return ovr_fps.IsLoaded(); } + bool TimecodesLoaded() const { return video_fps.IsLoaded() || ovr_fps.IsLoaded(); }; - const agi::vfr::Framerate& FPS() const { return ovrFPS.IsLoaded() ? ovrFPS : videoFPS; } - const agi::vfr::Framerate& VideoFPS() const { return videoFPS; } + const agi::vfr::Framerate& FPS() const { return ovr_fps.IsLoaded() ? ovr_fps : video_fps; } + const agi::vfr::Framerate& VideoFPS() const { return video_fps; } int TimeAtFrame(int frame, agi::vfr::Time type = agi::vfr::EXACT) const; int FrameAtTime(int time, agi::vfr::Time type = agi::vfr::EXACT) const; static VideoContext *Get(); - static void OnExit(); }; diff --git a/aegisub/src/video_display.cpp b/aegisub/src/video_display.cpp index ce1963b89..834d32650 100644 --- a/aegisub/src/video_display.cpp +++ b/aegisub/src/video_display.cpp @@ -54,6 +54,7 @@ #include "ass_file.h" #include "command/command.h" +#include "compat.h" #include "include/aegisub/context.h" #include "include/aegisub/hotkey.h" #include "include/aegisub/menu.h" @@ -75,7 +76,7 @@ int attribList[] = { WX_GL_RGBA , WX_GL_DOUBLEBUFFER, WX_GL_STENCIL_SIZE, 8, 0 } class OpenGlException : public agi::Exception { public: OpenGlException(const char *func, int err) - : agi::Exception(from_wx(wxString::Format("%s failed with error code %d", func, err))) + : agi::Exception(from_wx(wxString::Format("%s failed with error code %d", func, err))) { } const char * GetName() const { return "video/opengl"; } Exception * Copy() const { return new OpenGlException(*this); } @@ -432,5 +433,5 @@ void VideoDisplay::Unload() { } void VideoDisplay::OnSubtitlesSave() { - con->ass->SetScriptInfo("Video Zoom Percent", wxString::Format("%g", zoomValue)); + con->ass->SetScriptInfo("Video Zoom Percent", std::to_string(zoomValue)); } diff --git a/aegisub/src/video_display.h b/aegisub/src/video_display.h index e0a5fbca8..e65dee5c4 100644 --- a/aegisub/src/video_display.h +++ b/aegisub/src/video_display.h @@ -47,7 +47,7 @@ // Prototypes class AegiVideoFrame; -class FrameReadyEvent; +struct FrameReadyEvent; class VideoContext; class VideoOutGL; class VisualToolBase; diff --git a/aegisub/src/video_out_gl.h b/aegisub/src/video_out_gl.h index 222c85ffd..a362bda6d 100644 --- a/aegisub/src/video_out_gl.h +++ b/aegisub/src/video_out_gl.h @@ -36,8 +36,6 @@ #include -#include "compat.h" - class AegiVideoFrame; /// @class VideoOutGL @@ -104,22 +102,22 @@ DEFINE_BASE_EXCEPTION_NOINNER(VideoOutException, agi::Exception) /// @class VideoOutRenderException /// @extends VideoOutException -/// @brief An OpenGL error occured while uploading or displaying a frame +/// @brief An OpenGL error occurred while uploading or displaying a frame class VideoOutRenderException : public VideoOutException { public: VideoOutRenderException(const char *func, int err) - : VideoOutException(from_wx(wxString::Format("%s failed with error code %d", func, err))) + : VideoOutException(std::string(func) + " failed with error code " + std::to_string(err)) { } const char * GetName() const { return "videoout/opengl/render"; } Exception * Copy() const { return new VideoOutRenderException(*this); } }; /// @class VideoOutOpenGLException /// @extends VideoOutException -/// @brief An OpenGL error occured while setting up the video display +/// @brief An OpenGL error occurred while setting up the video display class VideoOutInitException : public VideoOutException { public: VideoOutInitException(const char *func, int err) - : VideoOutException(from_wx(wxString::Format("%s failed with error code %d", func, err))) + : VideoOutException(std::string(func) + " failed with error code " + std::to_string(err)) { } VideoOutInitException(const char *err) : VideoOutException(err) { } const char * GetName() const { return "videoout/opengl/init"; } diff --git a/aegisub/src/video_provider_avs.cpp b/aegisub/src/video_provider_avs.cpp index 3fd4a0df1..c551c20ac 100644 --- a/aegisub/src/video_provider_avs.cpp +++ b/aegisub/src/video_provider_avs.cpp @@ -38,43 +38,40 @@ #include "video_provider_avs.h" -#include -#include +#include "options.h" +#include "standard_paths.h" + +#include +#include +#include +#include + +#include +#include #ifdef _WIN32 #include #endif -#include - -#include "charset_conv.h" -#include "compat.h" -#include "options.h" -#include "standard_paths.h" - -AvisynthVideoProvider::AvisynthVideoProvider(wxString filename) +AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename) : last_fnum(-1) { + agi::acs::CheckFileRead(filename); + iframe.flipped = true; iframe.invertChannels = true; - wxMutexLocker lock(avs.GetMutex()); - - wxFileName fname(filename); - if (!fname.FileExists()) - throw agi::FileNotFoundError(from_wx(filename)); - - wxString extension = filename.Right(4).Lower(); + std::lock_guard lock(avs.GetMutex()); #ifdef _WIN32 - if (extension == ".avi") { + if (agi::fs::HasExtension(filename, "avi")) { // Try to read the keyframes before actually opening the file as trying // to open the file while it's already open can cause problems with // badly written VFW decoders AVIFileInit(); PAVIFILE pfile; - long hr = AVIFileOpen(&pfile, filename.wc_str(), OF_SHARE_DENY_WRITE, 0); + long hr = AVIFileOpen(&pfile, filename.c_str(), OF_SHARE_DENY_WRITE, 0); if (hr) { warning = "Unable to open AVI file for reading keyframes:\n"; switch (hr) { @@ -123,16 +120,14 @@ AvisynthVideoProvider::AvisynthVideoProvider(wxString filename) goto stream_release; } - bool all_keyframes = true; for (size_t i = 0; i < avis.dwLength; i++) { if (AVIStreamIsKeyFrame(ppavi, i)) KeyFrames.push_back(i); - else - all_keyframes = false; } // If every frame is a keyframe then just discard the keyframe data as it's useless - if (all_keyframes) KeyFrames.clear(); + if (KeyFrames.size() == (size_t)avis.dwLength) + KeyFrames.clear(); // Clean up stream_release: @@ -145,7 +140,7 @@ file_exit: #endif try { - AVSValue script = Open(fname, extension); + auto script = Open(filename); // Check if video was loaded properly if (!script.IsClip() || !script.AsClip()->GetVideoInfo().HasVideo()) @@ -154,7 +149,6 @@ file_exit: vi = script.AsClip()->GetVideoInfo(); if (!vi.IsRGB()) { /// @todo maybe read ColorMatrix hints for d2v files? - AVSValue args[2] = { script, "Rec601" }; if (!OPT_GET("Video/Force BT.601")->GetBool() && (vi.width > 1024 || vi.height >= 600)) { args[1] = "Rec709"; @@ -181,19 +175,19 @@ AvisynthVideoProvider::~AvisynthVideoProvider() { iframe.Clear(); } -AVSValue AvisynthVideoProvider::Open(wxFileName const& fname, wxString const& extension) { +AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) { IScriptEnvironment *env = avs.GetEnv(); - char *videoFilename = env->SaveString(fname.GetShortPath().mb_str(csConvLocal)); + char *videoFilename = env->SaveString(agi::fs::ShortName(filename).c_str()); // Avisynth file, just import it - if (extension == ".avs") { + if (agi::fs::HasExtension(filename, "avs")) { LOG_I("avisynth/video") << "Opening .avs file with Import"; decoderName = "Avisynth/Import"; return env->Invoke("Import", videoFilename); } // Open avi file with AviSource - if (extension == ".avi") { + if (agi::fs::HasExtension(filename, "avi")) { LOG_I("avisynth/video") << "Opening .avi file with AviSource"; try { const char *argnames[2] = { 0, "audio" }; @@ -209,9 +203,9 @@ AVSValue AvisynthVideoProvider::Open(wxFileName const& fname, wxString const& ex } // Open d2v with mpeg2dec3 - if (extension == ".d2v" && env->FunctionExists("Mpeg2Dec3_Mpeg2Source")) { + if (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("Mpeg2Dec3_Mpeg2Source")) { LOG_I("avisynth/video") << "Opening .d2v file with Mpeg2Dec3_Mpeg2Source"; - AVSValue script = env->Invoke("Mpeg2Dec3_Mpeg2Source", videoFilename); + auto script = env->Invoke("Mpeg2Dec3_Mpeg2Source", videoFilename); decoderName = "Avisynth/Mpeg2Dec3_Mpeg2Source"; //if avisynth is 2.5.7 beta 2 or newer old mpeg2decs will crash without this @@ -223,7 +217,7 @@ AVSValue AvisynthVideoProvider::Open(wxFileName const& fname, wxString const& ex } // If that fails, try opening it with DGDecode - if (extension == ".d2v" && env->FunctionExists("DGDecode_Mpeg2Source")) { + if (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("DGDecode_Mpeg2Source")) { LOG_I("avisynth/video") << "Opening .d2v file with DGDecode_Mpeg2Source"; decoderName = "DGDecode_Mpeg2Source"; return env->Invoke("Avisynth/Mpeg2Source", videoFilename); @@ -232,7 +226,7 @@ AVSValue AvisynthVideoProvider::Open(wxFileName const& fname, wxString const& ex // ancient but no sane person would use that anyway } - if (extension == ".d2v" && env->FunctionExists("Mpeg2Source")) { + if (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("Mpeg2Source")) { LOG_I("avisynth/video") << "Opening .d2v file with other Mpeg2Source"; AVSValue script = env->Invoke("Mpeg2Source", videoFilename); decoderName = "Avisynth/Mpeg2Source"; @@ -246,10 +240,9 @@ AVSValue AvisynthVideoProvider::Open(wxFileName const& fname, wxString const& ex // Try loading DirectShowSource2 if (!env->FunctionExists("dss2")) { - wxFileName dss2path(StandardPaths::DecodePath("?data/avss.dll")); - if (dss2path.FileExists()) { - env->Invoke("LoadPlugin", env->SaveString(dss2path.GetFullPath().mb_str(csConvLocal))); - } + auto dss2path(StandardPaths::DecodePath("?data/avss.dll")); + if (agi::fs::FileExists(dss2path)) + env->Invoke("LoadPlugin", env->SaveString(agi::fs::ShortName(dss2path).c_str())); } // If DSS2 loaded properly, try using it @@ -261,10 +254,9 @@ AVSValue AvisynthVideoProvider::Open(wxFileName const& fname, wxString const& ex // Try DirectShowSource // Load DirectShowSource.dll from app dir if it exists - wxFileName dsspath(StandardPaths::DecodePath("?data/DirectShowSource.dll")); - if (dsspath.FileExists()) { - env->Invoke("LoadPlugin",env->SaveString(dsspath.GetFullPath().mb_str(csConvLocal))); - } + auto dsspath(StandardPaths::DecodePath("?data/DirectShowSource.dll")); + if (agi::fs::FileExists(dsspath)) + env->Invoke("LoadPlugin", env->SaveString(agi::fs::ShortName(dsspath).c_str())); // Then try using DSS if (env->FunctionExists("DirectShowSource")) { @@ -284,9 +276,9 @@ AVSValue AvisynthVideoProvider::Open(wxFileName const& fname, wxString const& ex const AegiVideoFrame AvisynthVideoProvider::GetFrame(int n) { if (n == last_fnum) return iframe; - wxMutexLocker lock(avs.GetMutex()); + std::lock_guard lock(avs.GetMutex()); - PVideoFrame frame = RGB32Video->GetFrame(n, avs.GetEnv()); + auto frame = RGB32Video->GetFrame(n, avs.GetEnv()); iframe.pitch = frame->GetPitch(); iframe.w = frame->GetRowSize() / (vi.BitsPerPixel() / 8); iframe.h = frame->GetHeight(); diff --git a/aegisub/src/video_provider_avs.h b/aegisub/src/video_provider_avs.h index f8630cd32..abbc4929a 100644 --- a/aegisub/src/video_provider_avs.h +++ b/aegisub/src/video_provider_avs.h @@ -33,29 +33,30 @@ /// #ifdef WITH_AVISYNTH +#include "include/aegisub/video_provider.h" + #include "avisynth.h" #include "avisynth_wrap.h" -#include "include/aegisub/video_provider.h" #include "video_frame.h" class AvisynthVideoProvider: public VideoProvider { AviSynthWrapper avs; AegiVideoFrame iframe; - wxString decoderName; + std::string decoderName; agi::vfr::Framerate fps; std::vector KeyFrames; - wxString warning; - wxString colorspace; + std::string warning; + std::string colorspace; PClip RGB32Video; VideoInfo vi; int last_fnum; - AVSValue Open(wxFileName const& fname, wxString const& extension); + AVSValue Open(agi::fs::path const& filename); public: - AvisynthVideoProvider(wxString filename); + AvisynthVideoProvider(agi::fs::path const& filename); ~AvisynthVideoProvider(); const AegiVideoFrame GetFrame(int n); @@ -66,8 +67,8 @@ public: int GetHeight() const { return vi.height; }; double GetDAR() const { return 0; } std::vector GetKeyFrames() const { return KeyFrames; }; - wxString GetWarning() const { return warning; } - wxString GetDecoderName() const { return decoderName; } - wxString GetColorSpace() const { return colorspace; } + std::string GetWarning() const { return warning; } + std::string GetDecoderName() const { return decoderName; } + std::string GetColorSpace() const { return colorspace; } }; #endif diff --git a/aegisub/src/video_provider_cache.h b/aegisub/src/video_provider_cache.h index d72eb38bf..a5324d9de 100644 --- a/aegisub/src/video_provider_cache.h +++ b/aegisub/src/video_provider_cache.h @@ -67,7 +67,7 @@ public: double GetDAR() const { return master->GetDAR(); } agi::vfr::Framerate GetFPS() const { return master->GetFPS(); } std::vector GetKeyFrames() const { return master->GetKeyFrames(); } - wxString GetWarning() const { return master->GetWarning(); } - wxString GetDecoderName() const { return master->GetDecoderName(); } - wxString GetColorSpace() const { return master->GetColorSpace(); } + std::string GetWarning() const { return master->GetWarning(); } + std::string GetDecoderName() const { return master->GetDecoderName(); } + std::string GetColorSpace() const { return master->GetColorSpace(); } }; diff --git a/aegisub/src/video_provider_dummy.cpp b/aegisub/src/video_provider_dummy.cpp index 7fc2498fd..8d6c28c09 100644 --- a/aegisub/src/video_provider_dummy.cpp +++ b/aegisub/src/video_provider_dummy.cpp @@ -36,10 +36,16 @@ #include "video_provider_dummy.h" -#include - #include "colorspace.h" + #include +#include +#include + +#include +#include +#include +#include void DummyVideoProvider::Create(double fps, int frames, int width, int height, unsigned char red, unsigned char green, unsigned char blue, bool pattern) { this->framecount = frames; @@ -77,34 +83,30 @@ void DummyVideoProvider::Create(double fps, int frames, int width, int height, u } } -static long get_long(wxStringTokenizer &t, const char *err) { - long ret; - if (!t.GetNextToken().ToLong(&ret)) - throw VideoOpenError(err); - return ret; -} +DummyVideoProvider::DummyVideoProvider(agi::fs::path const& filename) { + if (!boost::starts_with(filename.string(), "?dummy")) + throw agi::fs::FileNotFound(std::string("Attempted creating dummy video provider with non-dummy filename")); -DummyVideoProvider::DummyVideoProvider(wxString const& filename) { - wxString params; - if (!filename.StartsWith("?dummy:", ¶ms)) - throw agi::FileNotFoundError("Attempted creating dummy video provider with non-dummy filename"); - - wxStringTokenizer t(params, ":"); - if (t.CountTokens() < 7) + std::vector toks; + auto const& fields = filename.string().substr(7); + boost::split(toks, fields, boost::is_any_of(":")); + if (toks.size() != 8) throw VideoOpenError("Too few fields in dummy video parameter list"); + size_t i = 0; double fps; - if (!t.GetNextToken().ToDouble(&fps)) - throw VideoOpenError("Unable to parse fps field in dummy video parameter list"); + int frames, width, height, red, green, blue; - long frames = get_long(t, "Unable to parse framecount field in dummy video parameter list"); - long width = get_long(t, "Unable to parse width field in dummy video parameter list"); - long height = get_long(t, "Unable to parse height field in dummy video parameter list"); - long red = get_long(t, "Unable to parse red colour field in dummy video parameter list"); - long green = get_long(t, "Unable to parse green colour field in dummy video parameter list"); - long blue = get_long(t, "Unable to parse blue colour field in dummy video parameter list"); + using agi::util::try_parse; + if (!try_parse(toks[i++], &fps)) throw VideoOpenError("Unable to parse fps field in dummy video parameter list"); + if (!try_parse(toks[i++], &frames)) throw VideoOpenError("Unable to parse framecount field in dummy video parameter list"); + if (!try_parse(toks[i++], &width)) throw VideoOpenError("Unable to parse width field in dummy video parameter list"); + if (!try_parse(toks[i++], &height)) throw VideoOpenError("Unable to parse height field in dummy video parameter list"); + if (!try_parse(toks[i++], &red)) throw VideoOpenError("Unable to parse red colour field in dummy video parameter list"); + if (!try_parse(toks[i++], &green)) throw VideoOpenError("Unable to parse green colour field in dummy video parameter list"); + if (!try_parse(toks[i++], &blue)) throw VideoOpenError("Unable to parse blue colour field in dummy video parameter list"); - bool pattern = t.GetNextToken() == "c"; + bool pattern = toks[i] == "c"; Create(fps, frames, width, height, red, green, blue, pattern); } @@ -117,6 +119,6 @@ DummyVideoProvider::~DummyVideoProvider() { frame.Clear(); } -wxString DummyVideoProvider::MakeFilename(double fps, int frames, int width, int height, agi::Color colour, bool pattern) { - return wxString::Format("?dummy:%f:%d:%d:%d:%d:%d:%d:%s", fps, frames, width, height, colour.r, colour.g, colour.b, pattern ? "c" : ""); +std::string DummyVideoProvider::MakeFilename(double fps, int frames, int width, int height, agi::Color colour, bool pattern) { + return str(boost::format("?dummy:%f:%d:%d:%d:%d:%d:%d:%s") % fps % frames % width % height % (int)colour.r % (int)colour.g % (int)colour.b % (pattern ? "c" : "")); } diff --git a/aegisub/src/video_provider_dummy.h b/aegisub/src/video_provider_dummy.h index bf7fdab71..bf6161ba0 100644 --- a/aegisub/src/video_provider_dummy.h +++ b/aegisub/src/video_provider_dummy.h @@ -64,7 +64,7 @@ class DummyVideoProvider : public VideoProvider { public: /// Create a dummy video from a string returned from MakeFilename - DummyVideoProvider(wxString const& filename); + DummyVideoProvider(agi::fs::path const& filename); /// Create a dummy video from separate parameters /// @param fps Frame rate of the dummy video @@ -80,7 +80,7 @@ public: /// Make a fake filename which when passed to the constructor taking a /// string will result in a video with the given parameters - static wxString MakeFilename(double fps, int frames, int width, int height, agi::Color colour, bool pattern); + static std::string MakeFilename(double fps, int frames, int width, int height, agi::Color colour, bool pattern); const AegiVideoFrame GetFrame(int n) { return frame; } int GetFrameCount() const { return framecount; } @@ -89,6 +89,6 @@ public: double GetDAR() const { return 0; } agi::vfr::Framerate GetFPS() const { return fps; } std::vector GetKeyFrames() const { return std::vector(); } - wxString GetColorSpace() const { return "None"; } - wxString GetDecoderName() const { return "Dummy Video Provider"; } + std::string GetColorSpace() const { return "None"; } + std::string GetDecoderName() const { return "Dummy Video Provider"; } }; diff --git a/aegisub/src/video_provider_ffmpegsource.cpp b/aegisub/src/video_provider_ffmpegsource.cpp index cd9f810c4..889b27fb5 100644 --- a/aegisub/src/video_provider_ffmpegsource.cpp +++ b/aegisub/src/video_provider_ffmpegsource.cpp @@ -37,21 +37,18 @@ #ifdef WITH_FFMS2 #include "video_provider_ffmpegsource.h" -#include - -#include -#include -#include - #include "aegisub_endian.h" #include "compat.h" #include "options.h" #include "utils.h" #include "video_context.h" -/// @brief Constructor -/// @param filename The filename to open -FFmpegSourceVideoProvider::FFmpegSourceVideoProvider(wxString filename) try +#include + +#include +#include + +FFmpegSourceVideoProvider::FFmpegSourceVideoProvider(agi::fs::path const& filename) try : VideoSource(nullptr, FFMS_DestroyVideoSource) , VideoInfo(nullptr) , Width(-1) @@ -65,30 +62,25 @@ FFmpegSourceVideoProvider::FFmpegSourceVideoProvider(wxString filename) try SetLogLevel(); - // and here we go LoadVideo(filename); } -catch (wxString const& err) { - throw VideoOpenError(from_wx(err)); +catch (std::string const& err) { + throw VideoOpenError(err); } catch (const char * err) { throw VideoOpenError(err); } -/// @brief Opens video -/// @param filename The filename to open -void FFmpegSourceVideoProvider::LoadVideo(wxString filename) { - wxString FileNameShort = wxFileName(filename).GetShortPath(); - - FFMS_Indexer *Indexer = FFMS_CreateIndexer(FileNameShort.utf8_str(), &ErrInfo); +void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const& filename) { + FFMS_Indexer *Indexer = FFMS_CreateIndexer(filename.string().c_str(), &ErrInfo); if (!Indexer) { if (ErrInfo.SubType == FFMS_ERROR_FILE_READ) - throw agi::FileNotFoundError(ErrInfo.Buffer); + throw agi::fs::FileNotFound(std::string(ErrInfo.Buffer)); else throw VideoNotSupported(ErrInfo.Buffer); } - std::map TrackList = GetTracksOfType(Indexer, FFMS_TYPE_VIDEO); + std::map TrackList = GetTracksOfType(Indexer, FFMS_TYPE_VIDEO); if (TrackList.size() <= 0) throw VideoNotSupported("no video tracks found"); @@ -103,13 +95,13 @@ void FFmpegSourceVideoProvider::LoadVideo(wxString filename) { } // generate a name for the cache file - wxString CacheName = GetCacheFilename(filename); + auto CacheName = GetCacheFilename(filename); // try to read index agi::scoped_holder - Index(FFMS_ReadIndex(CacheName.utf8_str(), &ErrInfo), FFMS_DestroyIndex); + Index(FFMS_ReadIndex(CacheName.string().c_str(), &ErrInfo), FFMS_DestroyIndex); - if (Index && FFMS_IndexBelongsToFile(Index, FileNameShort.utf8_str(), &ErrInfo)) + if (Index && FFMS_IndexBelongsToFile(Index, filename.string().c_str(), &ErrInfo)) Index = nullptr; // time to examine the index and check if the track we want is indexed @@ -133,7 +125,7 @@ void FFmpegSourceVideoProvider::LoadVideo(wxString filename) { } // update access time of index file so it won't get cleaned away - wxFileName(CacheName).Touch(); + agi::fs::Touch(CacheName); // we have now read the index and may proceed with cleaning the index cache CleanCache(); @@ -159,7 +151,7 @@ void FFmpegSourceVideoProvider::LoadVideo(wxString filename) { else SeekMode = FFMS_SEEK_NORMAL; - VideoSource = FFMS_CreateVideoSource(FileNameShort.utf8_str(), TrackNumber, Index, Threads, SeekMode, &ErrInfo); + VideoSource = FFMS_CreateVideoSource(filename.string().c_str(), TrackNumber, Index, Threads, SeekMode, &ErrInfo); if (!VideoSource) throw VideoOpenError(std::string("Failed to open video track: ") + ErrInfo.Buffer); @@ -178,7 +170,7 @@ void FFmpegSourceVideoProvider::LoadVideo(wxString filename) { DAR = double(Width) / Height; // Assuming TV for unspecified - wxString ColorRange = TempFrame->ColorRange == FFMS_CR_JPEG ? "PC" : "TV"; + ColorSpace = TempFrame->ColorRange == FFMS_CR_JPEG ? "PC" : "TV"; int CS = TempFrame->ColorSpace; #if FFMS_VERSION >= ((2 << 24) | (17 << 16) | (1 << 8) | 0) @@ -195,20 +187,20 @@ void FFmpegSourceVideoProvider::LoadVideo(wxString filename) { ColorSpace = "None"; break; case FFMS_CS_BT709: - ColorSpace = wxString::Format("%s.709", ColorRange); + ColorSpace += ".709"; break; case FFMS_CS_UNSPECIFIED: - ColorSpace = wxString::Format("%s.%s", ColorRange, Width > 1024 || Height >= 600 ? "709" : "601"); + ColorSpace += Width > 1024 || Height >= 600 ? "709" : "601"; break; case FFMS_CS_FCC: - ColorSpace = wxString::Format("%s.FCC", ColorRange); + ColorSpace += ".FCC"; break; case FFMS_CS_BT470BG: case FFMS_CS_SMPTE170M: - ColorSpace = wxString::Format("%s.601", ColorRange); + ColorSpace += ".601"; break; case FFMS_CS_SMPTE240M: - ColorSpace = wxString::Format("%s.240M", ColorRange); + ColorSpace += ".240M"; break; default: throw VideoOpenError("Unknown video color space"); @@ -228,15 +220,12 @@ void FFmpegSourceVideoProvider::LoadVideo(wxString filename) { if (TimeBase == nullptr) throw VideoOpenError("failed to get track time base"); - const FFMS_FrameInfo *CurFrameData; - // build list of keyframes and timecodes std::vector TimecodesVector; for (int CurFrameNum = 0; CurFrameNum < VideoInfo->NumFrames; CurFrameNum++) { - CurFrameData = FFMS_GetFrameInfo(FrameData, CurFrameNum); - if (CurFrameData == nullptr) { - throw VideoOpenError(from_wx(wxString::Format("Couldn't get info about frame %d", CurFrameNum))); - } + const FFMS_FrameInfo *CurFrameData = FFMS_GetFrameInfo(FrameData, CurFrameNum); + if (!CurFrameData) + throw VideoOpenError("Couldn't get info about frame " + std::to_string(CurFrameNum)); // keyframe? if (CurFrameData->KeyFrame) @@ -259,9 +248,8 @@ const AegiVideoFrame FFmpegSourceVideoProvider::GetFrame(int n) { // decode frame const FFMS_Frame *SrcFrame = FFMS_GetFrame(VideoSource, FrameNumber, &ErrInfo); - if (SrcFrame == nullptr) { + if (!SrcFrame) throw VideoDecodeError(std::string("Failed to retrieve frame: ") + ErrInfo.Buffer); - } CurFrame.SetTo(SrcFrame->Data[0], Width, Height, SrcFrame->Linesize[0]); return CurFrame; diff --git a/aegisub/src/video_provider_ffmpegsource.h b/aegisub/src/video_provider_ffmpegsource.h index d23559a1b..d99d716ba 100644 --- a/aegisub/src/video_provider_ffmpegsource.h +++ b/aegisub/src/video_provider_ffmpegsource.h @@ -52,17 +52,17 @@ class FFmpegSourceVideoProvider : public VideoProvider, FFmpegSourceProvider { int FrameNumber; ///< current framenumber std::vector KeyFramesList; ///< list of keyframes agi::vfr::Framerate Timecodes; ///< vfr object - wxString ColorSpace; ///< Colorspace name + std::string ColorSpace; ///< Colorspace name AegiVideoFrame CurFrame; ///< current video frame char FFMSErrMsg[1024]; ///< FFMS error message FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages - void LoadVideo(wxString filename); + void LoadVideo(agi::fs::path const& filename); public: - FFmpegSourceVideoProvider(wxString filename); + FFmpegSourceVideoProvider(agi::fs::path const& filename); const AegiVideoFrame GetFrame(int n); @@ -72,12 +72,12 @@ public: double GetDAR() const { return DAR; } agi::vfr::Framerate GetFPS() const { return Timecodes; } - wxString GetColorSpace() const { return ColorSpace; } + std::string GetColorSpace() const { return ColorSpace; } /// @brief Gets a list of keyframes /// @return Returns a wxArrayInt of keyframes. std::vector GetKeyFrames() const { return KeyFramesList; }; - wxString GetDecoderName() const { return "FFmpegSource"; } + std::string GetDecoderName() const { return "FFmpegSource"; } /// @brief Gets the desired cache behavior. /// @return Returns true. bool WantsCaching() const { return true; } diff --git a/aegisub/src/video_provider_manager.cpp b/aegisub/src/video_provider_manager.cpp index 7f630f6c6..994d50975 100644 --- a/aegisub/src/video_provider_manager.cpp +++ b/aegisub/src/video_provider_manager.cpp @@ -34,79 +34,66 @@ #include "config.h" -#include +#include "video_provider_manager.h" -#include "compat.h" #include "options.h" - -#ifdef WITH_AVISYNTH #include "video_provider_avs.h" -#endif #include "video_provider_cache.h" #include "video_provider_dummy.h" -#ifdef WITH_FFMS2 #include "video_provider_ffmpegsource.h" -#endif -#include "video_provider_manager.h" #include "video_provider_yuv4mpeg.h" +#include +#include -/// @brief Get provider -/// @param video -/// @return -/// -VideoProvider *VideoProviderFactory::GetProvider(wxString video) { - std::vector list = GetClasses(OPT_GET("Video/Provider")->GetString()); - if (video.StartsWith("?dummy")) list.insert(list.begin(), "Dummy"); - list.insert(list.begin(), "YUV4MPEG"); +VideoProvider *VideoProviderFactory::GetProvider(agi::fs::path const& video_file) { + std::vector factories = GetClasses(OPT_GET("Video/Provider")->GetString()); + factories.insert(factories.begin(), "YUV4MPEG"); + factories.insert(factories.begin(), "Dummy"); - bool fileFound = false; - bool fileSupported = false; + bool found = false; + bool supported = false; std::string errors; errors.reserve(1024); - for (auto const& factory : list) { + + for (auto const& factory : factories) { std::string err; try { - VideoProvider *provider = Create(factory, video); - LOG_I("manager/video/provider") << factory << ": opened " << from_wx(video); - if (provider->WantsCaching()) { - return new VideoProviderCache(provider); - } - return provider; + VideoProvider *provider = Create(factory, video_file); + LOG_I("manager/video/provider") << factory << ": opened " << video_file; + return provider->WantsCaching() ? new VideoProviderCache(provider) : provider; } - catch (agi::FileNotFoundError const&) { - err = factory + ": file not found."; + catch (agi::fs::FileNotFound const&) { + err = "file not found."; // Keep trying other providers as this one may just not be able to // open a valid path } catch (VideoNotSupported const&) { - fileFound = true; - err = factory + ": video is not in a supported format."; + found = true; + err = "video is not in a supported format."; } catch (VideoOpenError const& ex) { - fileSupported = true; - err = factory + ": " + ex.GetMessage(); + supported = true; + err = ex.GetMessage(); } catch (agi::vfr::Error const& ex) { - fileSupported = true; - err = factory + ": " + ex.GetMessage(); + supported = true; + err = ex.GetMessage(); } - errors += err; - errors += "\n"; - LOG_D("manager/video/provider") << err; + + errors += factory + ": " + err + "\n"; + LOG_D("manager/video/provider") << factory << ": " << err; } // No provider could open the file - LOG_E("manager/video/provider") << "Could not open " << from_wx(video); - std::string msg = "Could not open " + from_wx(video) + ":\n" + errors; + LOG_E("manager/video/provider") << "Could not open " << video_file; + std::string msg = "Could not open " + video_file.string() + ":\n" + errors; - if (!fileFound) throw agi::FileNotFoundError(from_wx(video)); - if (!fileSupported) throw VideoNotSupported(msg); + if (!found) throw agi::fs::FileNotFound(video_file.string()); + if (!supported) throw VideoNotSupported(msg); throw VideoOpenError(msg); } -/// @brief Register all providers -/// void VideoProviderFactory::RegisterProviders() { #ifdef WITH_AVISYNTH Register("Avisynth"); @@ -118,4 +105,4 @@ void VideoProviderFactory::RegisterProviders() { Register("YUV4MPEG", true); } -template<> VideoProviderFactory::map *FactoryBase::classes = nullptr; +template<> VideoProviderFactory::map *FactoryBase::classes = nullptr; diff --git a/aegisub/src/video_provider_manager.h b/aegisub/src/video_provider_manager.h index 8a5893d8d..739960746 100644 --- a/aegisub/src/video_provider_manager.h +++ b/aegisub/src/video_provider_manager.h @@ -1,42 +1,26 @@ -// Copyright (c) 2006-2008, Rodrigo Braz Monteiro, Fredrik Mellbin -// All rights reserved. +// Copyright (c) 2013, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of the Aegisub Group nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. +// 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/ -/// @file video_provider_manager.h -/// @see video_provider_manager.cpp -/// @ingroup video_input -/// - #include "factory_manager.h" #include "include/aegisub/video_provider.h" -class VideoProviderFactory : public Factory1 { +#include + +class VideoProviderFactory : public Factory1 { public: - static VideoProvider *GetProvider(wxString video); + static VideoProvider *GetProvider(agi::fs::path const& video_file); static void RegisterProviders(); }; diff --git a/aegisub/src/video_provider_yuv4mpeg.cpp b/aegisub/src/video_provider_yuv4mpeg.cpp index 00afd65b5..9b633a1fc 100644 --- a/aegisub/src/video_provider_yuv4mpeg.cpp +++ b/aegisub/src/video_provider_yuv4mpeg.cpp @@ -34,14 +34,19 @@ #include "config.h" -#include - #include "video_provider_yuv4mpeg.h" #include "compat.h" #include "utils.h" #include "video_frame.h" +#include +#include +#include + +#include +#include + // All of this cstdio bogus is because of one reason and one reason only: // MICROSOFT'S IMPLEMENTATION OF STD::FSTREAM DOES NOT SUPPORT FILES LARGER THAN 2 GB. // (yes, really) @@ -53,7 +58,7 @@ /// @brief Constructor /// @param filename The filename to open -YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(wxString fname) +YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(agi::fs::path const& filename) : sf(nullptr) , inited(false) , w (0) @@ -67,15 +72,13 @@ YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(wxString fname) fps_rat.den = 1; try { - wxString filename = wxFileName(fname).GetShortPath(); - #ifdef WIN32 - sf = _wfopen(filename.wc_str(), L"rb"); + sf = _wfopen(filename.c_str(), L"rb"); #else - sf = fopen(filename.utf8_str(), "rb"); + sf = fopen(filename.c_str(), "rb"); #endif - if (sf == nullptr) throw agi::FileNotFoundError(from_wx(fname)); + if (!sf) throw agi::fs::FileNotFound(filename); CheckFileFormat(); @@ -120,10 +123,9 @@ YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(wxString fname) } } - /// @brief Destructor YUV4MPEGVideoProvider::~YUV4MPEGVideoProvider() { - if (sf) fclose(sf); + fclose(sf); } /// @brief Checks if the file is an YUV4MPEG file or not @@ -143,14 +145,14 @@ void YUV4MPEGVideoProvider::CheckFileFormat() { /// @param startpos The byte offset at where to start reading /// @param reset_pos If true, the function will reset the file position to what it was before the function call before returning /// @return A list of parameters -std::vector YUV4MPEGVideoProvider::ReadHeader(int64_t startpos) { - std::vector tags; - wxString curtag; +std::vector YUV4MPEGVideoProvider::ReadHeader(int64_t startpos) { + std::vector tags; + std::string curtag; int bytesread = 0; int buf; if (fseeko(sf, startpos, SEEK_SET)) - throw VideoOpenError(from_wx(wxString::Format("YUV4MPEG video provider: ReadHeader: failed seeking to position %d", startpos))); + throw VideoOpenError("YUV4MPEG video provider: ReadHeader: failed seeking to position " + std::to_string(startpos)); // read header until terminating newline (0x0A) is found while ((buf = fgetc(sf)) != 0x0A) { @@ -166,29 +168,27 @@ std::vector YUV4MPEGVideoProvider::ReadHeader(int64_t startpos) { throw VideoOpenError("ReadHeader: Malformed header (no terminating newline found)"); // found a new tag - if (buf == 0x20) { + if (buf == 0x20 && !curtag.empty()) { tags.push_back(curtag); - curtag.Clear(); + curtag.clear(); } else - curtag.Append(static_cast(buf)); + curtag.push_back(buf); } // if only one tag with no trailing space was found (possible in the // FRAME header case), make sure we get it - if (!curtag.IsEmpty()) { + if (!curtag.empty()) tags.push_back(curtag); - curtag.Clear(); - } return tags; } /// @brief Parses a list of parameters and sets reader state accordingly /// @param tags The list of parameters to parse -void YUV4MPEGVideoProvider::ParseFileHeader(const std::vector& tags) { +void YUV4MPEGVideoProvider::ParseFileHeader(const std::vector& tags) { if (tags.size() <= 1) throw VideoOpenError("ParseFileHeader: contentless header"); - if (tags.front().Cmp("YUV4MPEG2")) + if (tags.front() != "YUV4MPEG2") throw VideoOpenError("ParseFileHeader: malformed header (bad magic)"); // temporary stuff @@ -200,30 +200,30 @@ void YUV4MPEGVideoProvider::ParseFileHeader(const std::vector& tags) { Y4M_PixelFormat t_pixfmt = Y4M_PIXFMT_NONE; for (unsigned i = 1; i < tags.size(); i++) { - wxString tag; - long tmp_long1 = 0; - long tmp_long2 = 0; + char type = tags[i][0]; + std::string tag = tags[i].substr(1); - if (tags[i].StartsWith("W", &tag)) { - if (!tag.ToLong(&tmp_long1)) + if (type == 'W') { + if (!agi::util::try_parse(tag, &t_w)) throw VideoOpenError("ParseFileHeader: invalid width"); - t_w = (int)tmp_long1; } - else if (tags[i].StartsWith("H", &tag)) { - if (!tag.ToLong(&tmp_long1)) + else if (type == 'H') { + if (!agi::util::try_parse(tag, &t_h)) throw VideoOpenError("ParseFileHeader: invalid height"); - t_h = (int)tmp_long1; } - else if (tags[i].StartsWith("F", &tag)) { - if (!(tag.BeforeFirst(':').ToLong(&tmp_long1) && tag.AfterFirst(':').ToLong(&tmp_long2))) + else if (type == 'F') { + size_t pos = tag.find(':'); + if (pos == tag.npos) + throw VideoOpenError("ParseFileHeader: invalid framerate"); + + if (!agi::util::try_parse(tag.substr(0, pos), &t_fps_num) || + !agi::util::try_parse(tag.substr(pos + 1), &t_fps_den)) throw VideoOpenError("ParseFileHeader: invalid framerate"); - t_fps_num = (int)tmp_long1; - t_fps_den = (int)tmp_long2; } - else if (tags[i].StartsWith("C", &tag)) { + else if (type == 'C') { // technically this should probably be case sensitive, // but being liberal in what you accept doesn't hurt - tag.MakeLower(); + boost::to_lower(tag); if (tag == "420") t_pixfmt = Y4M_PIXFMT_420JPEG; // is this really correct? else if (tag == "420jpeg") t_pixfmt = Y4M_PIXFMT_420JPEG; else if (tag == "420mpeg2") t_pixfmt = Y4M_PIXFMT_420MPEG2; @@ -236,8 +236,8 @@ void YUV4MPEGVideoProvider::ParseFileHeader(const std::vector& tags) { else throw VideoOpenError("ParseFileHeader: invalid or unknown colorspace"); } - else if (tags[i].StartsWith("I", &tag)) { - tag.MakeLower(); + else if (type == 'I') { + boost::to_lower(tag); if (tag == "p") t_imode = Y4M_ILACE_PROGRESSIVE; else if (tag == "t") t_imode = Y4M_ILACE_TFF; else if (tag == "b") t_imode = Y4M_ILACE_BFF; @@ -247,7 +247,7 @@ void YUV4MPEGVideoProvider::ParseFileHeader(const std::vector& tags) { throw VideoOpenError("ParseFileHeader: invalid or unknown interlacing mode"); } else - LOG_D("provider/video/yuv4mpeg") << "Unparsed tag: " << from_wx(tags[i]); + LOG_D("provider/video/yuv4mpeg") << "Unparsed tag: " << tags[i]; } // The point of all this is to allow multiple YUV4MPEG2 headers in a single file @@ -282,8 +282,8 @@ void YUV4MPEGVideoProvider::ParseFileHeader(const std::vector& tags) { /// @param tags The list of parameters to parse /// @return The flags set, as a binary mask /// This function is currently unimplemented (it will always return Y4M_FFLAG_NONE). -YUV4MPEGVideoProvider::Y4M_FrameFlags YUV4MPEGVideoProvider::ParseFrameHeader(const std::vector& tags) { - if (tags.front().Cmp("FRAME")) +YUV4MPEGVideoProvider::Y4M_FrameFlags YUV4MPEGVideoProvider::ParseFrameHeader(const std::vector& tags) { + if (tags.front() != "FRAME") throw VideoOpenError("ParseFrameHeader: malformed frame header (bad magic)"); /// @todo implement parsing of frame flags @@ -307,7 +307,7 @@ int YUV4MPEGVideoProvider::IndexFile() { throw VideoOpenError("IndexFile: ftello failed"); // continue reading headers until no more are found - std::vector tags = ReadHeader(curpos); + std::vector tags = ReadHeader(curpos); curpos = ftello(sf); if (curpos < 0) throw VideoOpenError("IndexFile: ftello failed"); @@ -316,11 +316,11 @@ int YUV4MPEGVideoProvider::IndexFile() { break; // no more headers Y4M_FrameFlags flags = Y4M_FFLAG_NOTSET; - if (!tags.front().Cmp("YUV4MPEG2")) { + if (tags.front() == "YUV4MPEG2") { ParseFileHeader(tags); continue; } - else if (!tags.front().Cmp("FRAME")) + else if (tags.front() == "FRAME") flags = ParseFrameHeader(tags); if (flags == Y4M_FFLAG_NONE) { @@ -328,7 +328,7 @@ int YUV4MPEGVideoProvider::IndexFile() { seek_table.push_back(curpos); // seek to next frame header start position if (fseeko(sf, frame_sz, SEEK_CUR)) - throw VideoOpenError(from_wx(wxString::Format("IndexFile: failed seeking to position %d", curpos + frame_sz))); + throw VideoOpenError("IndexFile: failed seeking to position " + std::to_string(curpos + frame_sz)); } else { /// @todo implement rff flags etc diff --git a/aegisub/src/video_provider_yuv4mpeg.h b/aegisub/src/video_provider_yuv4mpeg.h index dae7415fc..4f9263467 100644 --- a/aegisub/src/video_provider_yuv4mpeg.h +++ b/aegisub/src/video_provider_yuv4mpeg.h @@ -33,13 +33,10 @@ /// #include "include/aegisub/video_provider.h" + #include - #include -#include -#include - /// the maximum allowed header length, in bytes #define YUV4MPEG_HEADER_MAXLEN 128 @@ -103,7 +100,6 @@ class YUV4MPEGVideoProvider : public VideoProvider { Y4M_FFLAG_C_UNKNOWN = 0x0800 /// unknown (only allowed for non-4:2:0 sampling) }; - FILE *sf; /// source file bool inited; /// initialization state @@ -128,13 +124,13 @@ class YUV4MPEGVideoProvider : public VideoProvider { std::vector seek_table; void CheckFileFormat(); - void ParseFileHeader(const std::vector& tags); - Y4M_FrameFlags ParseFrameHeader(const std::vector& tags); - std::vector ReadHeader(int64_t startpos); + void ParseFileHeader(const std::vector& tags); + Y4M_FrameFlags ParseFrameHeader(const std::vector& tags); + std::vector ReadHeader(int64_t startpos); int IndexFile(); public: - YUV4MPEGVideoProvider(wxString filename); + YUV4MPEGVideoProvider(agi::fs::path const& filename); ~YUV4MPEGVideoProvider(); const AegiVideoFrame GetFrame(int n); @@ -145,7 +141,7 @@ public: double GetDAR() const { return 0; } agi::vfr::Framerate GetFPS() const { return fps; } std::vector GetKeyFrames() const { return std::vector(); } - wxString GetColorSpace() const { return "TV.601"; } - wxString GetDecoderName() const { return "YU4MPEG"; } + std::string GetColorSpace() const { return "TV.601"; } + std::string GetDecoderName() const { return "YU4MPEG"; } bool WantsCaching() const { return true; } }; diff --git a/aegisub/src/visual_tool.cpp b/aegisub/src/visual_tool.cpp index ad6d37b0a..ff87dfd7f 100644 --- a/aegisub/src/visual_tool.cpp +++ b/aegisub/src/visual_tool.cpp @@ -20,21 +20,17 @@ #include "config.h" -#include - #include "visual_tool.h" #include "ass_dialogue.h" #include "ass_file.h" #include "ass_style.h" #include "ass_time.h" -#include "compat.h" #include "include/aegisub/context.h" #include "options.h" #include "utils.h" #include "video_context.h" #include "video_display.h" -#include "video_provider_manager.h" #include "visual_feature.h" #include "visual_tool_clip.h" #include "visual_tool_drag.h" @@ -42,6 +38,9 @@ #include +#include +#include + using std::placeholders::_1; const wxColour VisualToolBase::colour[4] = {wxColour(106,32,19), wxColour(255,169,40), wxColour(255,253,185), wxColour(187,0,0)}; @@ -388,7 +387,7 @@ Vector2D VisualToolBase::GetLinePosition(AssDialogue *diag) { memcpy(margin, diag->Margin, sizeof margin); int align = 2; - if (AssStyle *style = c->ass->GetStyle(from_wx(diag->Style))) { + if (AssStyle *style = c->ass->GetStyle(diag->Style)) { align = style->alignment; for (int i = 0; i < 3; i++) { if (margin[i] == 0) @@ -453,7 +452,7 @@ bool VisualToolBase::GetLineMove(AssDialogue *diag, Vector2D &p1, Vector2D &p2, void VisualToolBase::GetLineRotation(AssDialogue *diag, float &rx, float &ry, float &rz) { rx = ry = rz = 0.f; - if (AssStyle *style = c->ass->GetStyle(from_wx(diag->Style))) + if (AssStyle *style = c->ass->GetStyle(diag->Style)) rz = style->angle; boost::ptr_vector blocks(diag->ParseTags()); @@ -471,7 +470,7 @@ void VisualToolBase::GetLineRotation(AssDialogue *diag, float &rx, float &ry, fl void VisualToolBase::GetLineScale(AssDialogue *diag, Vector2D &scale) { float x = 100.f, y = 100.f; - if (AssStyle *style = c->ass->GetStyle(from_wx(diag->Style))) { + if (AssStyle *style = c->ass->GetStyle(diag->Style)) { x = style->scalex; y = style->scaley; } @@ -506,7 +505,7 @@ void VisualToolBase::GetLineClip(AssDialogue *diag, Vector2D &p1, Vector2D &p2, } } -wxString VisualToolBase::GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse) { +std::string VisualToolBase::GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse) { boost::ptr_vector blocks(diag->ParseTags()); scale = 1; @@ -519,26 +518,26 @@ wxString VisualToolBase::GetLineVectorClip(AssDialogue *diag, int &scale, bool & tag = find_tag(blocks, "\\clip"); if (tag && tag->size() == 4) { - return wxString::Format("m %d %d l %d %d %d %d %d %d", - (*tag)[0].Get(), (*tag)[1].Get(), - (*tag)[2].Get(), (*tag)[1].Get(), - (*tag)[2].Get(), (*tag)[3].Get(), - (*tag)[0].Get(), (*tag)[3].Get()); + return str(boost::format("m %d %d l %d %d %d %d %d %d") + % (*tag)[0].Get() % (*tag)[1].Get() + % (*tag)[2].Get() % (*tag)[1].Get() + % (*tag)[2].Get() % (*tag)[3].Get() + % (*tag)[0].Get() % (*tag)[3].Get()); } if (tag) { scale = std::max((*tag)[0].Get(scale), 1); - return to_wx((*tag)[1].Get("")); + return (*tag)[1].Get(""); } return ""; } -void VisualToolBase::SetSelectedOverride(std::string const& tag, wxString const& value) { +void VisualToolBase::SetSelectedOverride(std::string const& tag, std::string const& value) { for (auto line : c->selectionController->GetSelectedSet()) SetOverride(line, tag, value); } -void VisualToolBase::SetOverride(AssDialogue* line, std::string const& tag, wxString const& value) { +void VisualToolBase::SetOverride(AssDialogue* line, std::string const& tag, std::string const& value) { if (!line) return; std::string removeTag; @@ -562,12 +561,12 @@ void VisualToolBase::SetOverride(AssDialogue* line, std::string const& tag, wxSt i--; } } - ovr->AddTag(tag + from_wx(value)); + ovr->AddTag(tag + value); line->UpdateText(blocks); } else - line->Text = "{" + to_wx(tag) + value + "}" + line->Text; + line->Text = "{" + tag + value + "}" + line->Text.get(); } // If only export worked diff --git a/aegisub/src/visual_tool.h b/aegisub/src/visual_tool.h index efe3c5cd3..18d2a7ea7 100644 --- a/aegisub/src/visual_tool.h +++ b/aegisub/src/visual_tool.h @@ -28,9 +28,7 @@ #include -#include #include -#include #include @@ -133,10 +131,10 @@ protected: void GetLineRotation(AssDialogue *diag, float &rx, float &ry, float &rz); void GetLineScale(AssDialogue *diag, Vector2D &scale); void GetLineClip(AssDialogue *diag, Vector2D &p1, Vector2D &p2, bool &inverse); - wxString GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse); + std::string GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse); - void SetOverride(AssDialogue* line, std::string const& tag, wxString const& value); - void SetSelectedOverride(std::string const& tag, wxString const& value); + void SetOverride(AssDialogue* line, std::string const& tag, std::string const& value); + void SetSelectedOverride(std::string const& tag, std::string const& value); VisualToolBase(VideoDisplay *parent, agi::Context *context); diff --git a/aegisub/src/visual_tool_clip.cpp b/aegisub/src/visual_tool_clip.cpp index aebab905c..a5dd5552b 100644 --- a/aegisub/src/visual_tool_clip.cpp +++ b/aegisub/src/visual_tool_clip.cpp @@ -20,8 +20,6 @@ #include "config.h" -#include - #include "visual_tool_clip.h" #include "ass_dialogue.h" @@ -29,6 +27,9 @@ #include "selection_controller.h" #include "utils.h" +#include +#include + VisualToolClip::VisualToolClip(VideoDisplay *parent, agi::Context *context) : VisualTool(parent, context) , cur_1(0, 0) @@ -112,12 +113,12 @@ void VisualToolClip::UpdateHold() { } void VisualToolClip::CommitHold() { - wxString value = wxString::Format("(%s,%s)", ToScriptCoords(cur_1.Min(cur_2)).Str(), ToScriptCoords(cur_1.Max(cur_2)).Str()); + std::string value = str(boost::format("(%s,%s)") % ToScriptCoords(cur_1.Min(cur_2)).Str() % ToScriptCoords(cur_1.Max(cur_2)).Str()); for (auto line : c->selectionController->GetSelectedSet()) { // This check is technically not correct as it could be outside of an // override block... but that's rather unlikely - bool has_iclip = line->Text.get().find("\\iclip") != wxString::npos; + bool has_iclip = line->Text.get().find("\\iclip") != std::string::npos; SetOverride(line, has_iclip ? "\\iclip" : "\\clip", value); } } diff --git a/aegisub/src/visual_tool_cross.cpp b/aegisub/src/visual_tool_cross.cpp index c3984e80c..64b4a8951 100644 --- a/aegisub/src/visual_tool_cross.cpp +++ b/aegisub/src/visual_tool_cross.cpp @@ -26,6 +26,10 @@ #include "include/aegisub/context.h" #include "video_display.h" +#include + +#include + VisualToolCross::VisualToolCross(VideoDisplay *parent, agi::Context *context) : VisualTool(parent, context) , gl_text(new OpenGLText) @@ -45,9 +49,9 @@ void VisualToolCross::OnDoubleClick() { int t1, t2; if (GetLineMove(line, p1, p2, t1, t2)) { if (t1 > 0 || t2 > 0) - SetOverride(line, "\\move", wxString::Format("(%s,%s,%d,%d)", Text(p1 + d), Text(p2 + d), t1, t2)); + SetOverride(line, "\\move", str(boost::format("(%s,%s,%d,%d)") % Text(p1 + d) % Text(p2 + d) % t1 % t2)); else - SetOverride(line, "\\move", wxString::Format("(%s,%s)", Text(p1 + d), Text(p2 + d))); + SetOverride(line, "\\move", str(boost::format("(%s,%s)") % Text(p1 + d) % Text(p2 + d))); } else SetOverride(line, "\\pos", "(" + Text(GetLinePosition(line) + d) + ")"); @@ -74,11 +78,11 @@ void VisualToolCross::Draw() { gl.DrawLines(2, lines, 4); gl.ClearInvert(); - wxString mouse_text = Text(ToScriptCoords(shift_down ? video_res - mouse_pos : mouse_pos)); + std::string mouse_text = Text(ToScriptCoords(shift_down ? video_res - mouse_pos : mouse_pos)); int tw, th; gl_text->SetFont("Verdana", 12, true, false); - gl_text->SetColour(*wxWHITE, 1.f); + gl_text->SetColour(agi::Color(255, 255, 255, 255)); gl_text->GetExtent(mouse_text, tw, th); // Place the text in the corner of the cross closest to the center of the video @@ -97,6 +101,6 @@ void VisualToolCross::Draw() { gl_text->Print(mouse_text, dx, dy); } -wxString VisualToolCross::Text(Vector2D v) { +std::string VisualToolCross::Text(Vector2D v) { return video_res.X() > script_res.X() ? v.Str() : v.DStr(); } diff --git a/aegisub/src/visual_tool_cross.h b/aegisub/src/visual_tool_cross.h index 75c7e2259..9579dd03d 100644 --- a/aegisub/src/visual_tool_cross.h +++ b/aegisub/src/visual_tool_cross.h @@ -34,7 +34,7 @@ class VisualToolCross : public VisualTool { void OnDoubleClick(); void Draw(); - wxString Text(Vector2D v); + std::string Text(Vector2D v); public: VisualToolCross(VideoDisplay *parent, agi::Context *context); ~VisualToolCross(); diff --git a/aegisub/src/visual_tool_drag.cpp b/aegisub/src/visual_tool_drag.cpp index 29c3b97c4..9b2378f2d 100644 --- a/aegisub/src/visual_tool_drag.cpp +++ b/aegisub/src/visual_tool_drag.cpp @@ -22,12 +22,6 @@ #include "visual_tool_drag.h" -#include -#include - -#include -#include - #include "ass_dialogue.h" #include "ass_file.h" #include "include/aegisub/context.h" @@ -39,6 +33,12 @@ #include +#include +#include +#include + +#include + static const DraggableFeatureType DRAG_ORIGIN = DRAG_BIG_TRIANGLE; static const DraggableFeatureType DRAG_START = DRAG_BIG_SQUARE; static const DraggableFeatureType DRAG_END = DRAG_BIG_CIRCLE; @@ -94,7 +94,7 @@ void VisualToolDrag::OnSubTool(wxCommandEvent &) { // Round the start and end times to exact frames int start = vc->TimeAtFrame(vc->FrameAtTime(line->Start, agi::vfr::START)) - line->Start; int end = vc->TimeAtFrame(vc->FrameAtTime(line->Start, agi::vfr::END)) - line->Start; - SetOverride(line, "\\move", wxString::Format("(%s,%s,%d,%d)", p1.Str(), p1.Str(), start, end)); + SetOverride(line, "\\move", str(boost::format("(%s,%s,%d,%d)") % p1.Str() % p1.Str() % start % end)); } } @@ -293,10 +293,10 @@ void VisualToolDrag::UpdateDrag(feature_iterator feature) { SetOverride(feature->line, "\\pos", ToScriptCoords(feature->pos).PStr()); else SetOverride(feature->line, "\\move", - wxString::Format("(%s,%s,%d,%d)", - ToScriptCoords(feature->pos).Str(), - ToScriptCoords(end_feature->pos).Str(), - feature->time, end_feature->time)); + str(boost::format("(%s,%s,%d,%d)") + % ToScriptCoords(feature->pos).Str() + % ToScriptCoords(end_feature->pos).Str() + % feature->time % end_feature->time)); } void VisualToolDrag::OnDoubleClick() { @@ -307,9 +307,9 @@ void VisualToolDrag::OnDoubleClick() { int t1, t2; if (GetLineMove(line, p1, p2, t1, t2)) { if (t1 > 0 || t2 > 0) - SetOverride(line, "\\move", wxString::Format("(%s,%s,%d,%d)", (p1 + d).Str(), (p2 + d).Str(), t1, t2)); + SetOverride(line, "\\move", str(boost::format("(%s,%s,%d,%d)") % (p1 + d).Str() % (p2 + d).Str() % t1 % t2)); else - SetOverride(line, "\\move", wxString::Format("(%s,%s)", (p1 + d).Str(), (p2 + d).Str())); + SetOverride(line, "\\move", str(boost::format("(%s,%s)") % (p1 + d).Str() % (p2 + d).Str())); } else SetOverride(line, "\\pos", (GetLinePosition(line) + d).PStr()); diff --git a/aegisub/src/visual_tool_rotatexy.cpp b/aegisub/src/visual_tool_rotatexy.cpp index 703977f46..353d4a6e6 100644 --- a/aegisub/src/visual_tool_rotatexy.cpp +++ b/aegisub/src/visual_tool_rotatexy.cpp @@ -20,10 +20,11 @@ #include "config.h" -#include - #include "visual_tool_rotatexy.h" +#include +#include + VisualToolRotateXY::VisualToolRotateXY(VideoDisplay *parent, agi::Context *context) : VisualTool(parent, context) , angle_x(0) @@ -156,8 +157,8 @@ void VisualToolRotateXY::UpdateHold() { angle_x = fmodf(angle_x + 360.f, 360.f); angle_y = fmodf(angle_y + 360.f, 360.f); - SetSelectedOverride("\\frx", wxString::Format("%.4g", angle_x)); - SetSelectedOverride("\\fry", wxString::Format("%.4g", angle_y)); + SetSelectedOverride("\\frx", str(boost::format("%.4g") % angle_x)); + SetSelectedOverride("\\fry", str(boost::format("%.4g") % angle_y)); } void VisualToolRotateXY::UpdateDrag(feature_iterator feature) { diff --git a/aegisub/src/visual_tool_rotatez.cpp b/aegisub/src/visual_tool_rotatez.cpp index 2473e9117..8ff490953 100644 --- a/aegisub/src/visual_tool_rotatez.cpp +++ b/aegisub/src/visual_tool_rotatez.cpp @@ -20,12 +20,13 @@ #include "config.h" -#include - #include "visual_tool_rotatez.h" #include "utils.h" +#include +#include + static const float deg2rad = 3.1415926536f / 180.f; static const float rad2deg = 180.f / 3.1415926536f; @@ -114,7 +115,7 @@ void VisualToolRotateZ::UpdateHold() { angle = fmodf(angle + 360.f, 360.f); - SetSelectedOverride("\\frz", wxString::Format("%.4g", angle)); + SetSelectedOverride("\\frz", str(boost::format("%.4g") % angle)); } void VisualToolRotateZ::UpdateDrag(feature_iterator feature) { diff --git a/aegisub/src/visual_tool_scale.cpp b/aegisub/src/visual_tool_scale.cpp index bb68b6c9f..3fd2395cd 100644 --- a/aegisub/src/visual_tool_scale.cpp +++ b/aegisub/src/visual_tool_scale.cpp @@ -103,8 +103,8 @@ void VisualToolScale::UpdateHold() { if (ctrl_down) scale = scale.Round(25.f); - SetSelectedOverride("\\fscx", wxString::Format("%d", (int)scale.X())); - SetSelectedOverride("\\fscy", wxString::Format("%d", (int)scale.Y())); + SetSelectedOverride("\\fscx", std::to_string((int)scale.X())); + SetSelectedOverride("\\fscy", std::to_string((int)scale.Y())); } void VisualToolScale::DoRefresh() { diff --git a/aegisub/src/visual_tool_vector_clip.cpp b/aegisub/src/visual_tool_vector_clip.cpp index 5039396c7..600f337a9 100644 --- a/aegisub/src/visual_tool_vector_clip.cpp +++ b/aegisub/src/visual_tool_vector_clip.cpp @@ -18,14 +18,10 @@ /// @brief Vector clipping visual typesetting tool /// @ingroup visual_ts -#include "visual_tool_vector_clip.h" - -#include - -#include - #include "config.h" +#include "visual_tool_vector_clip.h" + #include "ass_dialogue.h" #include "include/aegisub/context.h" #include "libresrc/libresrc.h" @@ -33,6 +29,9 @@ #include "selection_controller.h" #include "utils.h" +#include +#include + /// Button IDs enum { BUTTON_DRAG = 1300, @@ -190,15 +189,15 @@ void VisualToolVectorClip::MakeFeatures() { } void VisualToolVectorClip::Save() { - wxString value = "("; + std::string value = "("; if (spline.GetScale() != 1) - value += wxString::Format("%d,", spline.GetScale()); + value += std::to_string(spline.GetScale()) + ","; value += spline.EncodeToAss() + ")"; for (auto line : c->selectionController->GetSelectedSet()) { // This check is technically not correct as it could be outside of an // override block... but that's rather unlikely - bool has_iclip = line->Text.get().find("\\iclip") != wxString::npos; + bool has_iclip = line->Text.get().find("\\iclip") != std::string::npos; SetOverride(line, has_iclip ? "\\iclip" : "\\clip", value); } } @@ -388,9 +387,8 @@ void VisualToolVectorClip::UpdateHold() { void VisualToolVectorClip::DoRefresh() { if (!active_line) return; - wxString vect; int scale; - vect = GetLineVectorClip(active_line, scale, inverse); + std::string vect = GetLineVectorClip(active_line, scale, inverse); spline.SetScale(scale); spline.DecodeFromAss(vect); diff --git a/aegisub/tests/Makefile b/aegisub/tests/Makefile index b2b6359f0..06041676a 100644 --- a/aegisub/tests/Makefile +++ b/aegisub/tests/Makefile @@ -10,7 +10,7 @@ CPPFLAGS += -I../src/include -I../libaegisub/include $(CFLAGS_ICONV) -I${GTEST_R CXXFLAGS += -Wno-unused-value ifeq (yes, $(BUILD_DARWIN)) -LDFLAGS += -framework ApplicationServices +LDFLAGS += -framework ApplicationServices -framework Foundation endif SRC = \ @@ -28,6 +28,7 @@ SRC = \ libaegisub_line_wrap.cpp \ libaegisub_mru.cpp \ libaegisub_option.cpp \ + libaegisub_path.cpp \ libaegisub_signals.cpp \ libaegisub_syntax_highlight.cpp \ libaegisub_thesaurus.cpp \ diff --git a/aegisub/tests/libaegisub_access.cpp b/aegisub/tests/libaegisub_access.cpp index dd58350c7..82a42b77e 100644 --- a/aegisub/tests/libaegisub_access.cpp +++ b/aegisub/tests/libaegisub_access.cpp @@ -11,92 +11,69 @@ // 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. -// -// $Id$ - -/// @file libaegisub_access.cpp -/// @brief agi::acs tests. -/// @ingroup acs - -#include #include "main.h" +#include +#include + using namespace agi; using namespace agi::acs; - -class lagi_acs : public libagi { - -protected: - // place holder for future code placement -}; - +using namespace agi::fs; // Yes, this is a horrifying use of macros, since these are all void static // methods I couldn't think of a better way to test these without massive code // duplication. -#define EX_FileNotFoundError(func, pass) \ - TEST_F(lagi_acs, func##ExFileNotFoundError) { \ - EXPECT_THROW(func("data/nonexistent"), FileNotFoundError); \ +#define DO_TEST(func, name, fail, fail_ex, pass) \ + TEST(lagi_acs, name) { \ + EXPECT_THROW(func(fail), fail_ex); \ EXPECT_NO_THROW(func(pass)); \ } +#define EX_FileNotFound(func, pass) \ + DO_TEST(func, func##ExFileNotFound, "data/nonexistent", FileNotFound, pass) + #define EX_Fatal(func, fail, pass) \ - TEST_F(lagi_acs, func##ExFatal) { \ - EXPECT_THROW(func(fail), Fatal); \ - EXPECT_NO_THROW(func(pass)); \ - } + DO_TEST(func, func##ExFatal, fail, Fatal, pass) #define EX_NotAFile(func, fail, pass) \ - TEST_F(lagi_acs, func##ExNotAFile) { \ - EXPECT_THROW(func(fail), NotAFile); \ - EXPECT_NO_THROW(func(pass)); \ - } + DO_TEST(func, func##ExNotAFile, fail, NotAFile, pass) #define EX_NotADirectory(func, fail, pass) \ - TEST_F(lagi_acs, func##ExNotADirectory) { \ - EXPECT_THROW(func(fail), NotADirectory); \ - EXPECT_NO_THROW(func(pass)); \ - } + DO_TEST(func, func##ExNotADirectory, fail, NotADirectory, pass) #define EX_Read(func, fail, pass) \ - TEST_F(lagi_acs, func##ExRead) { \ - EXPECT_THROW(func(fail), Read); \ - EXPECT_NO_THROW(func(pass)); \ - } + DO_TEST(func, func##ExReadDenied, fail, ReadDenied, pass) #define EX_Write(func, fail, pass) \ - TEST_F(lagi_acs, func##ExWrite) { \ - EXPECT_THROW(func(fail), Write); \ - EXPECT_NO_THROW(func(pass)); \ - } + DO_TEST(func, func##ExWriteDenied, fail, WriteDenied, pass) -EX_FileNotFoundError(CheckFileRead, "data/file") +EX_FileNotFound(CheckFileRead, "data/file") EX_Read(CheckFileRead, "data/file_access_denied", "data/file") EX_NotAFile(CheckFileRead, "data/dir", "data/file") -TEST_F(lagi_acs, CheckFileRead) { +TEST(lagi_acs, CheckFileRead) { EXPECT_NO_THROW(CheckFileRead("data/file")); } -EX_FileNotFoundError(CheckFileWrite, "data/file") +EX_FileNotFound(CheckFileWrite, "data/file") EX_Read(CheckFileWrite, "data/file_access_denied", "data/file") EX_NotAFile(CheckFileWrite, "data/dir", "data/file") EX_Write(CheckFileWrite, "data/file_read_only", "data/file") -TEST_F(lagi_acs, CheckFileWrite) { +TEST(lagi_acs, CheckFileWrite) { EXPECT_NO_THROW(CheckFileRead("data/file")); } -EX_FileNotFoundError(CheckDirRead, "data/dir") +EX_FileNotFound(CheckDirRead, "data/dir") EX_Read(CheckDirRead, "data/dir_access_denied", "data/dir") EX_NotADirectory(CheckDirRead, "data/file", "data/dir") -TEST_F(lagi_acs, CheckDirRead) { +TEST(lagi_acs, CheckDirRead) { EXPECT_NO_THROW(CheckDirRead("data/dir")); } -EX_FileNotFoundError(CheckDirWrite, "data/dir") +EX_FileNotFound(CheckDirWrite, "data/dir") EX_Read(CheckDirWrite, "data/dir_access_denied", "data/dir") EX_NotADirectory(CheckDirWrite, "data/file", "data/dir") EX_Write(CheckDirWrite, "data/dir_read_only", "data/dir") -TEST_F(lagi_acs, CheckDirWrite) { +TEST(lagi_acs, CheckDirWrite) { EXPECT_NO_THROW(CheckDirWrite("data/dir")); } diff --git a/aegisub/tests/libaegisub_hotkey.cpp b/aegisub/tests/libaegisub_hotkey.cpp index 0466b4b91..ae9388d56 100644 --- a/aegisub/tests/libaegisub_hotkey.cpp +++ b/aegisub/tests/libaegisub_hotkey.cpp @@ -11,14 +11,14 @@ // 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. -// -// $Id$ #include "main.h" -#include "util.h" +#include #include +#include + using namespace agi::hotkey; static const char simple_valid[] = "{" @@ -147,9 +147,8 @@ static void insert_combo(Hotkey::HotkeyMap &hm, const char *ctx, const char *cmd va_list argp; va_start(argp, N); - for (int i = 0; i < N; i++) { + for (int i = 0; i < N; ++i) keys[i] = va_arg(argp, const char *); - } va_end(argp); hm.insert(make_pair(std::string(cmd), Combo(ctx, cmd, keys))); @@ -158,7 +157,7 @@ static void insert_combo(Hotkey::HotkeyMap &hm, const char *ctx, const char *cmd static void set_var(bool *b) { *b = true; } TEST(lagi_hotkey, set_hotkey_map) { - util::remove("data/hotkey_tmp"); + agi::fs::Remove("data/hotkey_tmp"); { Hotkey h("data/hotkey_tmp", "{}"); diff --git a/aegisub/tests/libaegisub_iconv.cpp b/aegisub/tests/libaegisub_iconv.cpp index 070d68afe..5c30b4267 100644 --- a/aegisub/tests/libaegisub_iconv.cpp +++ b/aegisub/tests/libaegisub_iconv.cpp @@ -11,19 +11,11 @@ // 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. -// -// $Id$ - -/// @file libaegisub_iconv.cpp -/// @brief agi::charset -/// @ingroup iconv #include #include - #include "main.h" -#include "util.h" using namespace agi::charset; diff --git a/aegisub/tests/libaegisub_keyframe.cpp b/aegisub/tests/libaegisub_keyframe.cpp index 266ec0267..f11cf2e07 100644 --- a/aegisub/tests/libaegisub_keyframe.cpp +++ b/aegisub/tests/libaegisub_keyframe.cpp @@ -11,13 +11,12 @@ // 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. -// -// $Id$ /// @file libaegisub_keyframe.cpp /// @brief agi::keyframe tests /// @ingroup video_input +#include #include #include @@ -41,7 +40,7 @@ TEST(lagi_keyframe, save) { } TEST(lagi_keyframe, bad_files) { - EXPECT_THROW(Load(""), agi::FileSystemError); + EXPECT_THROW(Load(""), agi::fs::FileSystemError); EXPECT_THROW(Load("data/keyframe/empty.txt"), Error); EXPECT_THROW(Load("data/keyframe/garbage.txt"), Error); } diff --git a/aegisub/tests/libaegisub_line_iterator.cpp b/aegisub/tests/libaegisub_line_iterator.cpp index 900ae95df..83d0df366 100644 --- a/aegisub/tests/libaegisub_line_iterator.cpp +++ b/aegisub/tests/libaegisub_line_iterator.cpp @@ -11,12 +11,6 @@ // 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. -// -// $Id$ - -/// @file libaegisub_line_iterator.cpp -/// @brief agi::line_iterator tests -/// @ingroup #include diff --git a/aegisub/tests/libaegisub_line_wrap.cpp b/aegisub/tests/libaegisub_line_wrap.cpp index cd522fb33..edee7bd09 100644 --- a/aegisub/tests/libaegisub_line_wrap.cpp +++ b/aegisub/tests/libaegisub_line_wrap.cpp @@ -11,11 +11,6 @@ // 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. -// -// $Id$ - -/// @file libaegisub_line_wrap.cpp -/// @brief agi::get_wrap_points tests #include diff --git a/aegisub/tests/libaegisub_mru.cpp b/aegisub/tests/libaegisub_mru.cpp index 123aedbf3..803d2ffcb 100644 --- a/aegisub/tests/libaegisub_mru.cpp +++ b/aegisub/tests/libaegisub_mru.cpp @@ -11,16 +11,11 @@ // 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. -// -// $Id$ - -/// @file libaegisub_mru.cpp -/// @brief agi::mru (Most Recently Used) -/// @ingroup mru +#include #include + #include "main.h" -#include "util.h" class lagi_mru : public libagi { protected: @@ -34,56 +29,58 @@ protected: }; -TEST_F(lagi_mru, MRUConstructFromFile) { +TEST_F(lagi_mru, load_from_file) { ASSERT_NO_THROW(agi::MRUManager mru(conf_ok, default_mru)); agi::MRUManager mru(conf_ok, default_mru); - agi::MRUManager::MRUListMap::const_iterator entry = mru.Get("Valid")->begin(); - EXPECT_STREQ("Entry One", (*entry++).c_str()); - EXPECT_STREQ("Entry Two", (*entry++).c_str()); + ASSERT_NO_THROW(mru.Get("Valid")); + ASSERT_EQ(2u, mru.Get("Valid")->size()); + auto entry = mru.Get("Valid")->begin(); + EXPECT_STREQ("Entry One", (*entry++).string().c_str()); + EXPECT_STREQ("Entry Two", (*entry++).string().c_str()); EXPECT_TRUE(mru.Get("Valid")->end() == entry); } -TEST_F(lagi_mru, MRUConstructFromString) { - util::remove("data/mru_tmp"); +TEST_F(lagi_mru, load_from_default_string) { + agi::fs::Remove("data/mru_tmp"); agi::MRUManager mru("data/mru_tmp", default_mru); } -TEST_F(lagi_mru, MRUConstructInvalid) { - util::copy("data/mru_invalid.json", "data/mru_tmp"); +TEST_F(lagi_mru, load_from_invalid_file) { + agi::fs::Copy("data/mru_invalid.json", "data/mru_tmp"); agi::MRUManager mru("data/mru_tmp", default_mru); EXPECT_TRUE(mru.Get("Invalid")->empty()); } -TEST_F(lagi_mru, MRUEntryAdd) { - util::copy("data/mru_ok.json", "data/mru_tmp"); +TEST_F(lagi_mru, add_entry) { + agi::fs::Copy("data/mru_ok.json", "data/mru_tmp"); agi::MRUManager mru("data/mru_tmp", default_mru); EXPECT_NO_THROW(mru.Add("Valid", "/path/to/file")); - EXPECT_STREQ("/path/to/file", mru.Get("Valid")->front().c_str()); + EXPECT_STREQ("/path/to/file", mru.Get("Valid")->front().string().c_str()); } -TEST_F(lagi_mru, MRUEntryRemove) { - util::copy("data/mru_ok.json", "data/mru_tmp"); +TEST_F(lagi_mru, remove_entry) { + agi::fs::Copy("data/mru_ok.json", "data/mru_tmp"); agi::MRUManager mru("data/mru_tmp", default_mru); EXPECT_NO_THROW(mru.Add("Valid", "/path/to/file")); EXPECT_NO_THROW(mru.Remove("Valid", "/path/to/file")); - EXPECT_STRNE("/path/to/file", mru.Get("Valid")->front().c_str()); + EXPECT_STRNE("/path/to/file", mru.Get("Valid")->front().string().c_str()); } -TEST_F(lagi_mru, MRUKeyInvalid) { - util::copy("data/mru_ok.json", "data/mru_tmp"); +TEST_F(lagi_mru, invalid_mru_key_throws) { + agi::fs::Copy("data/mru_ok.json", "data/mru_tmp"); agi::MRUManager mru("data/mru_tmp", default_mru); EXPECT_THROW(mru.Add("Invalid", "/path/to/file"), agi::MRUErrorInvalidKey); EXPECT_THROW(mru.Get("Invalid"), agi::MRUErrorInvalidKey); } -TEST_F(lagi_mru, MRUKeyValid) { - util::copy("data/mru_ok.json", "data/mru_tmp"); +TEST_F(lagi_mru, valid_mru_key_doesnt_throw) { + agi::fs::Copy("data/mru_ok.json", "data/mru_tmp"); agi::MRUManager mru("data/mru_tmp", default_mru); EXPECT_NO_THROW(mru.Add("Valid", "/path/to/file")); } -TEST_F(lagi_mru, MRUAddSeveral) { - util::remove("data/mru_tmp"); +TEST_F(lagi_mru, adding_existing_moves_to_front) { + agi::fs::Remove("data/mru_tmp"); agi::MRUManager mru("data/mru_tmp", default_mru); EXPECT_NO_THROW(mru.Add("Valid", "/file/1")); @@ -93,9 +90,9 @@ TEST_F(lagi_mru, MRUAddSeveral) { EXPECT_NO_THROW(mru.Add("Valid", "/file/1")); EXPECT_NO_THROW(mru.Add("Valid", "/file/3")); - EXPECT_STREQ("/file/3", mru.GetEntry("Valid", 0).c_str()); - EXPECT_STREQ("/file/1", mru.GetEntry("Valid", 1).c_str()); - EXPECT_STREQ("/file/2", mru.GetEntry("Valid", 2).c_str()); + EXPECT_STREQ("/file/3", mru.GetEntry("Valid", 0).string().c_str()); + EXPECT_STREQ("/file/1", mru.GetEntry("Valid", 1).string().c_str()); + EXPECT_STREQ("/file/2", mru.GetEntry("Valid", 2).string().c_str()); EXPECT_THROW(mru.GetEntry("Valid", 3), agi::MRUErrorIndexOutOfRange); } diff --git a/aegisub/tests/libaegisub_option.cpp b/aegisub/tests/libaegisub_option.cpp index 9210986e9..3c093730d 100644 --- a/aegisub/tests/libaegisub_option.cpp +++ b/aegisub/tests/libaegisub_option.cpp @@ -11,13 +11,8 @@ // 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. -// -// $Id$ - -/// @file libaegisub_option.cpp -/// @brief Option class -/// @ingroup option +#include #include #include @@ -56,7 +51,7 @@ TEST_F(lagi_option, get_nonexistant_option) { } TEST_F(lagi_option, flush_skip) { - util::copy("data/options/string.json", "data/options/tmp"); + agi::fs::Copy("data/options/string.json", "data/options/tmp"); { agi::Options opt("data/options/tmp", default_opt, agi::Options::FLUSH_SKIP); ASSERT_NO_THROW(opt.Get("Valid")->SetString("")); @@ -65,7 +60,7 @@ TEST_F(lagi_option, flush_skip) { } TEST_F(lagi_option, flush_no_skip) { - util::copy("data/options/string.json", "data/options/tmp"); + agi::fs::Copy("data/options/string.json", "data/options/tmp"); { agi::Options opt("data/options/tmp", default_opt); ASSERT_NO_THROW(opt.Get("Valid")->SetString("")); @@ -124,7 +119,7 @@ TEST_F(lagi_option, heterogeneous_arrays_rejected) { } TEST_F(lagi_option, flush_roundtrip) { - util::remove("data/options/tmp"); + agi::fs::Remove("data/options/tmp"); { agi::Options opt("data/options/tmp", "{}"); diff --git a/aegisub/tests/libaegisub_path.cpp b/aegisub/tests/libaegisub_path.cpp new file mode 100644 index 000000000..b20a9e66d --- /dev/null +++ b/aegisub/tests/libaegisub_path.cpp @@ -0,0 +1,132 @@ +// Copyright (c) 2013, Thomas Goyne +// +// 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. + +#include +#include +#include + +#include "main.h" + +#include + +using agi::Path; + +#ifdef _WIN32 +#define DS "\\" +#else +#define DS "/" +#endif + +TEST(lagi_path, invalid_token_name_throws_internal_error) { + Path p; + + // These are InternalError because the tokens are currently always hardcoded + EXPECT_THROW(p.SetToken("no ?", "path"), agi::InternalError); + EXPECT_THROW(p.SetToken("?bad", "path"), agi::InternalError); +} + +TEST(lagi_path, relative_path_clears_token) { + Path p; + + EXPECT_NO_THROW(p.SetToken("?video", "relative/path")); + EXPECT_STREQ("?video", p.Decode("?video").string().c_str()); + + EXPECT_NO_THROW(p.SetToken("?video", boost::filesystem::current_path())); + EXPECT_STRNE("?video", p.Decode("?video").string().c_str()); + + EXPECT_NO_THROW(p.SetToken("?video", "relative/path")); + EXPECT_STREQ("?video", p.Decode("?video").string().c_str()); +} + +TEST(lagi_path, empty_path_clears_token) { + Path p; + + EXPECT_NO_THROW(p.SetToken("?video", boost::filesystem::current_path())); + EXPECT_STRNE("?video", p.Decode("?video").string().c_str()); + + EXPECT_NO_THROW(p.SetToken("?video", "")); + EXPECT_STREQ("?video", p.Decode("?video").string().c_str()); +} + +TEST(lagi_path, decode_sets_uses_right_slashes) { + Path p; + + agi::fs::path expected = boost::filesystem::current_path()/"foo/bar.txt"; + expected.make_preferred(); + + EXPECT_NO_THROW(p.SetToken("?video", boost::filesystem::current_path())); + + agi::fs::path decoded; + ASSERT_NO_THROW(decoded = p.Decode("?video/foo/bar.txt")); + EXPECT_STREQ(expected.string().c_str(), decoded.string().c_str()); +} + +TEST(lagi_path, trailing_slash_on_token_is_optional) { + Path p; + + agi::fs::path expected = boost::filesystem::current_path()/"foo.txt"; + expected.make_preferred(); + + EXPECT_NO_THROW(p.SetToken("?audio", boost::filesystem::current_path())); + + agi::fs::path decoded; + ASSERT_NO_THROW(decoded = p.Decode("?audiofoo.txt")); + EXPECT_STREQ(expected.string().c_str(), decoded.string().c_str()); + + ASSERT_NO_THROW(decoded = p.Decode("?audio/foo.txt")); + EXPECT_STREQ(expected.string().c_str(), decoded.string().c_str()); +} + +TEST(lagi_path, setting_token_to_file_sets_to_parent_directory_instead) { + Path p; + + agi::fs::path file = boost::filesystem::system_complete("data/file"); + ASSERT_NO_THROW(p.SetToken("?script", file)); + EXPECT_STREQ(file.parent_path().string().c_str(), p.Decode("?script").string().c_str()); + + file = boost::filesystem::system_complete("data/dir"); + ASSERT_NO_THROW(p.SetToken("?script", file)); + EXPECT_STREQ(file.string().c_str(), p.Decode("?script").string().c_str()); +} + +TEST(lagi_path, valid_token_names) { + Path p; + + EXPECT_NO_THROW(p.SetToken("?user", "")); + EXPECT_NO_THROW(p.SetToken("?local", "")); + EXPECT_NO_THROW(p.SetToken("?data", "")); + EXPECT_NO_THROW(p.SetToken("?temp", "")); + EXPECT_NO_THROW(p.SetToken("?dictionary", "")); + EXPECT_NO_THROW(p.SetToken("?docs", "")); + EXPECT_NO_THROW(p.SetToken("?audio", "")); + EXPECT_NO_THROW(p.SetToken("?script", "")); + EXPECT_NO_THROW(p.SetToken("?video", "")); +} + +#define TEST_PLATFORM_PATH_TOKEN(tok) \ + do { \ + agi::fs::path d; \ + ASSERT_NO_THROW(d = p.Decode(tok)); \ + ASSERT_FALSE(d.empty()); \ + ASSERT_STRNE(tok, d.string().c_str()); \ + EXPECT_TRUE(agi::fs::DirectoryExists(d)); \ + } while (false) + +TEST(lagi_path, platform_paths_have_values_and_exist) { + Path p; + TEST_PLATFORM_PATH_TOKEN("?data"); + TEST_PLATFORM_PATH_TOKEN("?user"); + TEST_PLATFORM_PATH_TOKEN("?local"); + TEST_PLATFORM_PATH_TOKEN("?temp"); +} diff --git a/aegisub/tests/libaegisub_signals.cpp b/aegisub/tests/libaegisub_signals.cpp index a56710ff8..1e7986323 100644 --- a/aegisub/tests/libaegisub_signals.cpp +++ b/aegisub/tests/libaegisub_signals.cpp @@ -11,12 +11,6 @@ // 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. -// -// $Id$ - -/// @file libaegisub_signals.cpp -/// @brief agi::signals tests -/// @ingroup #include diff --git a/aegisub/tests/libaegisub_thesaurus.cpp b/aegisub/tests/libaegisub_thesaurus.cpp index 6239613f9..9988c1e21 100644 --- a/aegisub/tests/libaegisub_thesaurus.cpp +++ b/aegisub/tests/libaegisub_thesaurus.cpp @@ -11,9 +11,8 @@ // 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. -// -// $Id$ +#include #include #include "main.h" diff --git a/aegisub/tests/libaegisub_util.cpp b/aegisub/tests/libaegisub_util.cpp index 0dcb08c5e..82af8e0d4 100644 --- a/aegisub/tests/libaegisub_util.cpp +++ b/aegisub/tests/libaegisub_util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2010, Amar Takhar +// Copyright (c) 2013, Thomas Goyne // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -12,125 +12,15 @@ // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // -// $Id$ +// Aegisub Project http://www.aegisub.org/ -/// @file libaegisub_util.cpp -/// @brief agi::util (Utilities) -/// @ingroup util - -#include -#include #include + #include "main.h" class lagi_util : public libagi { }; namespace agi { - -TEST(lagi_util, UtilDirnameEmpty) { - EXPECT_STREQ(".", util::DirName("").c_str()); -} - -TEST(lagi_util, UtilDirnameNoTrailingSlash) { - EXPECT_STREQ(".", util::DirName("dot").c_str()); -} - -TEST(lagi_util, UtilDirnameRoot) { - EXPECT_STREQ("/", util::DirName("/").c_str()); -} - -TEST(lagi_util, UtilDirnameHeir) { - EXPECT_STREQ("/last/part/not_stripped/", util::DirName("/last/part/not_stripped/").c_str()); -} - -TEST(lagi_util, UtilDirnameHeirNoTrailingSlash) { - EXPECT_STREQ("/last/part/", util::DirName("/last/part/stripped").c_str()); -} - -TEST(lagi_util, UtilRenameOverwrite) { - util::Rename("./data/rename_me_overwrite", "./data/rename_me_overwrite_renamed"); - util::Rename("./data/rename_me_overwrite_renamed", "./data/rename_me_overwrite"); - std::ofstream fp_touch("./data/rename_me_overwrite_renamed"); -} - -TEST(lagi_util, UtilRenameNew) { - util::Rename("./data/rename_me", "./data/rename_me_renamed"); - util::Rename("./data/rename_me_renamed", "./data/rename_me"); -} - -TEST(lagi_util, UtilRenameExNotFound) { - EXPECT_THROW(util::Rename("./data/nonexistent", ""), FileNotFoundError); -} - -TEST(lagi_util, RemoveExisting) { - std::ofstream("./data/file_to_remove"); - EXPECT_NO_THROW(util::Remove("./data/file_to_remove")); - std::ifstream check("./data/file_to_remove"); - EXPECT_FALSE(check.good()); -} - -TEST(lagi_util, RemoveNonExisting) { - EXPECT_NO_THROW(util::Remove("./data/nonexistent")); -} - -TEST(lagi_util, RemoveReadOnly) { - EXPECT_THROW(util::Remove("./data/file_read_only"), FileNotAccessibleError); -} - -TEST(lagi_util, Utilstr_lower) { - std::string str("-!ABCDEFGHIJKLMNOPQRSTUVWXYZ123"); - util::str_lower(str); - EXPECT_STREQ("-!abcdefghijklmnopqrstuvwxyz123", str.c_str()); -} - -TEST(lagi_util, UtilstrtoiInvalidRange) { - std::string str("2147483650"); - EXPECT_EQ(0, util::strtoi(str)); - - str.assign("-2147483650"); - EXPECT_EQ(0, util::strtoi(str)); -} - -TEST(lagi_util, UtilstrtoiInvalidString) { - std::string str("bottles of beer on the wall"); - EXPECT_EQ(0, util::strtoi(str)); -} - -TEST(lagi_util, UtilstrtoiNumberWithString) { - std::string str("24 bottles of beer on the wall"); - EXPECT_EQ(24, util::strtoi(str)); -} - -TEST(lagi_util, UtilstrtoiValidString) { - std::string str("24"); - int i; - - EXPECT_NO_THROW(i = util::strtoi(str)); - EXPECT_EQ(24, i); -} - -TEST(lagi_util, UtilfreespaceFile) { - std::string path("./data/somefile"); - EXPECT_NO_THROW(util::freespace(path, util::TypeFile)); - EXPECT_ANY_THROW(util::freespace(path)); - -} - -TEST(lagi_util, UtilfreespaceDir) { - std::string path("./data"); - EXPECT_NO_THROW(util::freespace(path)); -} - -TEST(lagi_util, UtilfreespaceNoAccess) { - std::string path("./data/dir_access_denied"); - EXPECT_THROW(util::freespace(path), acs::Read); -} - -TEST(lagi_util, UtilfreespaceInvalid) { - std::string path("/nonexistent"); - EXPECT_ANY_THROW(util::freespace(path)); -} - TEST(lagi_util, try_parse_double) { double d = 0.0; EXPECT_TRUE(util::try_parse("1.0", &d)); @@ -152,4 +42,4 @@ TEST(lagi_util, try_parse_int) { EXPECT_EQ(1.0, i); } -} // namespace agi +} diff --git a/aegisub/tests/libaegisub_vfr.cpp b/aegisub/tests/libaegisub_vfr.cpp index 212db9be6..78321c6cb 100644 --- a/aegisub/tests/libaegisub_vfr.cpp +++ b/aegisub/tests/libaegisub_vfr.cpp @@ -12,12 +12,9 @@ // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // -// $Id$ - -/// @file libaegisub_vfr.cpp -/// @brief agi::vfr::Framerate tests -/// @ingroup video_input +// Aegisub Project http://www.aegisub.org/ +#include #include #include diff --git a/aegisub/tests/main.cpp b/aegisub/tests/main.cpp index b96aeb15b..7344b24de 100644 --- a/aegisub/tests/main.cpp +++ b/aegisub/tests/main.cpp @@ -11,18 +11,16 @@ // 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. -// -// $Id$ - -/// @file main.cpp -/// @brief Main -/// @ingroup main #include +#include +#include #include int main(int argc, char **argv) { + agi::dispatch::Init([](agi::dispatch::Thunk f) { }); + int retval; agi::log::log = new agi::log::LogSink; agi::log::log->Subscribe(new agi::log::JsonEmitter("./", agi::log::log)); diff --git a/aegisub/tests/main.h b/aegisub/tests/main.h index 078e231c9..847c43898 100644 --- a/aegisub/tests/main.h +++ b/aegisub/tests/main.h @@ -11,24 +11,9 @@ // 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. -// -// $Id$ - -/// @file main.h -/// @brief Main header -/// @ingroup main #include -/// A small macro to silence "unused variable" warnings. -#define unused(x) x = x - namespace { - -class libagi : public ::testing::Test { -protected: - // place holder for future code placement -}; - -} // namespace - +class libagi : public ::testing::Test { }; +} diff --git a/aegisub/tests/util.cpp b/aegisub/tests/util.cpp index 5f6f12f0f..6061081d7 100644 --- a/aegisub/tests/util.cpp +++ b/aegisub/tests/util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2010, Amar Takhar +// Copyright (c) 2013, Thomas Goyne // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -12,25 +12,14 @@ // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // -// $Id$ +// Aegisub Project http://www.aegisub.org/ -/// @file util.cpp -/// @brief Common utilities used in tests. -/// @ingroup util common +#include "util.h" #include #include -#include "util.h" namespace util { - -void copy(const std::string &from, const std::string &to) { - std::ifstream ifs(from.c_str(), std::ios::binary); - std::ofstream ofs(to.c_str(), std::ios::binary); - - ofs << ifs.rdbuf(); -} - bool compare(const std::string &file1, const std::string &file2) { std::stringstream ss1, ss2; std::ifstream if1(file1.c_str(), std::ios::binary), if2(file2.c_str(), std::ios::binary); @@ -39,6 +28,4 @@ bool compare(const std::string &file1, const std::string &file2) { return ss1.str() == ss2.str(); } -} // namespace util - - +} diff --git a/aegisub/tests/util.h b/aegisub/tests/util.h index 411d4a38a..738b12ecb 100644 --- a/aegisub/tests/util.h +++ b/aegisub/tests/util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010, Amar Takhar +// Copyright (c) 2013, Thomas Goyne // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -12,11 +12,7 @@ // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // -// $Id$ - -/// @file util.cpp -/// @brief Common utilities used in tests. -/// @ingroup util +// Aegisub Project http://www.aegisub.org/ #include #include @@ -24,10 +20,7 @@ #include namespace util { - -void copy(const std::string &from, const std::string &to); -bool compare(const std::string &file1, const std::string &file2); -void remove(const std::string& file); +bool compare(std::string const& file1, std::string const& file2); template static std::vector make_vector(int len, ...) { @@ -42,6 +35,4 @@ static std::vector make_vector(int len, ...) { return vec; } -} // namespace util - - +}